﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.Serialization.Json;
using Microsoft.Win32;
using System.Xml.Serialization;
using SystemHelperLibrary.ShellExt;
using System.Security.Principal;
using System.Security.AccessControl;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Management;
using System.Threading;

namespace SystemHelperLibrary
{
	/// <summary>
	/// Repository contract
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public interface IRepository<T>
	{
		void Add(T item);
		void Update(T Item);
		void Delete(T item);

		IQueryable<T> GetAll();
		T GetSingle(object id);
		T GetSingle(T item);
	}

	/// <summary>
	/// Contract for Caching wrapper
	/// </summary>
	public interface ICache
	{
		/// <summary>
		/// Gets the number of items in the cache.
		/// </summary>
		int Count { get; }

		/// <summary>
		/// Gets a collection of all cache item keys.
		/// </summary>
		ICollection Keys { get; }

		/// <summary>
		/// Gets a collection of all cache items.
		/// </summary>
		ICollection Values { get; }

		/// <summary>
		/// Whether or not there is a cache entry for the specified key.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		bool Contains(string key);


		/// <summary>
		/// Retrieves an item from the cache.
		/// </summary>
		object Get(object key);


		/// <summary>
		/// Retrieves an item from the cache of the specified type.
		/// </summary>
		T Get<T>(object key);

		/// <summary>
		/// Retrieves an item from the cache of the specified type and key and 
		/// inserts by getting it using the lamda it if isn't there
		/// </summary>
		T GetOrInsert<T>(object key, Func<T> fetcher);


		/// <summary>
		/// Removes an item from the cache.
		/// </summary>
		void Remove(object key);


		/// <summary>
		/// Removes collection of items from the cache.
		/// </summary>
		void RemoveAll(ICollection keys);


		/// <summary>
		/// Removes all items from the cache.
		/// </summary>
		void Clear();


		/// <summary>
		/// Inserts an item into the cache.
		/// </summary>
		void Insert(object key, object value);
	}

	/// <summary>
	/// Singleton implementation using generics.
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class Singleton<T>
	{
		private static T _item;
		private static object _syncRoot = new object();
		private static Func<T> _creator;


		/// <summary>
		/// Prevent instantiation
		/// </summary>
		private Singleton() { }


		/// <summary>
		/// Initalize with the creator.
		/// </summary>
		/// <param name="creator"></param>
		public static void Init(Func<T> creator)
		{
			_creator = creator;
		}


		/// <summary>
		/// Initialize using instance.
		/// </summary>
		/// <param name="item"></param>
		public static void Init(T item)
		{
			_item = item;
			_creator = new Func<T>(() => item);
		}


		/// <summary>
		/// Get the instance of the singleton item T.
		/// </summary>
		public static T Instance
		{
			get
			{
				if (_creator == null)
					return default(T);

				if (_item == null)
				{
					lock (_syncRoot)
					{
						_item = _creator();
					}
				}

				return _item;
			}
		}
	}

	public sealed class DataErrorsChangedEventArgs : EventArgs
	{
		// Methods 
		public DataErrorsChangedEventArgs(string propertyName)
		{
			this.PropertyName = propertyName;
		}
		// Properties  
		public string PropertyName { get; private set; }
	}

	/// <summary>
	/// Notifier interface,serve to notify whethere errors occured
	/// </summary>
	public interface INotifyDataErrorInfo
	{
		event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
		IEnumerable GetErrors(string propertyName);
		bool HasErrors { get; }
	}

	public static class SystemHelper
	{
		[DllImport("wininet.dll")]
		private extern static bool InternetGetConnectedState(out int conDescriprion, int reservedValue);

		/// <summary>
		/// Indicate is inet connection available
		/// </summary>
		/// <returns>true if available</returns>
		public static bool IsConnectionAvailable()
		{
			int description = 0;

			return InternetGetConnectedState(out description, 0);
		}

		/// <summary>
		/// Is DbNull value
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static bool IsDbNull(this object obj)
		{
			return (obj == DBNull.Value);
		}

		public static bool Is<T>(this T value, params T[] candidates)
		{
			if (candidates == null) return false;

			foreach (var t in candidates)
			{
				if (value.Equals(t)) return true;
			}

			return false;
		}

		/// <summary>
		/// Raises an simple event that provides no data.
		/// </summary>
		public static void Raise(this EventHandler eventHandler, object sender)
		{
			var handler = eventHandler;
			if (handler != null) handler(sender, EventArgs.Empty);
		}

		/// <summary>
		/// Raises an event and submits the corresponding event arguments.
		/// </summary>
		public static void Raise<T>(this EventHandler<T> eventHandler, object sender, T eventArgs) where T : EventArgs
		{
			var handler = eventHandler;
			if (handler != null) handler(sender, eventArgs);
		}

		public static Assembly GetEmbededAssembly(string pathToAssembly)
		{
			Assembly assembly = Assembly.GetExecutingAssembly();
			string assemblyName = assembly.FullName.Split(',')[0];
			string resourceName = string.Format("{0}.{1}", assemblyName, pathToAssembly);
			Stream imageStream = assembly.GetManifestResourceStream(resourceName);
			long bytestreamMaxLength = imageStream.Length;
			byte[] buffer = new byte[bytestreamMaxLength];
			imageStream.Read(buffer, 0, (int)bytestreamMaxLength);
			return Assembly.Load(buffer);
		}

		public static void SaveEmbededdedResource(string resourceName, string fileName)
		{
			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
			{
				using (FileStream fileStream = new FileStream(fileName, FileMode.CreateNew))
				{
					for (int i = 0; i < stream.Length; i++)
						fileStream.WriteByte((byte)stream.ReadByte());
					fileStream.Flush();
				}
			}

		}

		public static bool Is64Bit()
		{
			return (Environment.GetEnvironmentVariable("ProgramFiles(x86)") != null);
		}

		private static UInt32 CountPhysicalProcessors()
		{
			//you must add a reference to the System.Management assembly
			ManagementObjectSearcher objects = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
			ManagementObjectCollection coll = objects.Get();
			foreach (ManagementObject obj in coll)
			{
				return (UInt32)obj["NumberOfProcessors"];
			}
			return 0;
		}

		private static UInt64 CountPhysicalMemory()
		{
			ManagementObjectSearcher objects = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMemory");
			ManagementObjectCollection coll = objects.Get();
			UInt64 total = 0;
			foreach (ManagementObject obj in coll)
			{
				total += (UInt64)obj["Capacity"];
			}
			return total;
		}

		public static List<string> GetMotherBoardInformation()
		{
			dynamic list = new List<string>();
			ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Product, SerialNumber FROM Win32_BaseBoard");
			// Executing the query...
			// Because the machine has a single Motherborad, then a single object (row) returned.
			ManagementObjectCollection information = searcher.Get(); foreach (ManagementObject obj in information)
			{
				// Retrieving the properties (columns) 
				// Writing column name then its value  
				foreach (PropertyData data in obj.Properties)
				{
					list.Add(string.Format("{0} = {1}", data.Name, data.Value));
				}
			}
			// For typical use of disposable objects enclose it in a using statement instead.
			searcher.Dispose();

			return list;
		}
	}

	public static class ExceptionHelpers
	{
		private delegate T ObjectActivator<T>(params object[] args);

		/// <summary>
		/// Throw exception if object is null
		/// </summary>
		/// <example>
		/// account.ThrowIfNull(typeof(InvalidAccountException)); 
		/// </example>
		/// <typeparam name="T"></typeparam>
		/// <param name="exception"></param>
		/// <param name="value"></param>
		public static void ThrowIfNull<T>(Type exception, T value) where T : class
		{
			if (value == null)
			{
				var types = new Type[2];
				types[0] = typeof(string); types[1] = typeof(Exception);
				var constructorInfo = exception.GetConstructor(types);
				var createdActivator = GetActivator<Exception>(constructorInfo);
				var instance = createdActivator(typeof(T).Name + " is null", null);
				throw instance;
			}
		}

		private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
		{
			Type type = ctor.DeclaringType;
			ParameterInfo[] paramsInfo = ctor.GetParameters();
			//create a single param of type object[]        
			ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
			var argsExp = new Expression[paramsInfo.Length];
			//pick each arg from the params array        
			//and create a typed expression of them        
			for (int i = 0; i < paramsInfo.Length; i++)
			{
				Expression index = Expression.Constant(i);
				Type paramType = paramsInfo[i].ParameterType;
				Expression paramAccessorExp = Expression.ArrayIndex(param, index);
				Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
				argsExp[i] = paramCastExp;
			}
			//make a NewExpression that calls the    
			//ctor with the args we just created      
			var newExp = Expression.New(ctor, argsExp);
			//create a lambda with the New         //Expression as body and our param object[] as arg 
			var lambda = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param);
			//compile it      
			var compiled = (ObjectActivator<T>)lambda.Compile();
			return compiled;
		}
	}

	/// <summary>
	/// Instance factory class
	/// </summary>
	public static class InstanceFactory
	{
		public static T CreateInstance<T>(params object[] args)
		{
			return (T)typeof(T).CreateInstance(args);
		}

		public static object CreateInstance(this Type type, params object[] args)
		{
			return Activator.CreateInstance(type, args);
		}
	}

	/// <summary>
	/// 	Generic exception for combining several other exceptions
	/// </summary>
	public class CombinedException : Exception
	{
		/// <summary>
		/// 	Initializes a new instance of the <see cref = "CombinedException" /> class.
		/// </summary>
		/// <param name = "message">The message.</param>
		/// <param name = "innerExceptions">The inner exceptions.</param>
		public CombinedException(string message, Exception[] innerExceptions)
			: base(message)
		{
			InnerExceptions = innerExceptions;
		}

		/// <summary>
		/// 	Gets the inner exceptions.
		/// </summary>
		/// <value>The inner exceptions.</value>
		public Exception[] InnerExceptions { get; protected set; }
	}

	[DataContract]
	public abstract class Changeable : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
		{
			ContractExt.Contract.Requires(args != null);
			PropertyChangedEventHandler handler = this.PropertyChanged;
			if (handler != null)
			{
				handler(this, args);
			}
		}

		protected void OnPropertyChanged(string propertyName)
		{
			ContractExt.Contract.Requires(!string.IsNullOrWhiteSpace(propertyName));
			this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
		}

		/// <summary>       
		/// /// Helper method for raising the PropertyChanged event.   
		/// /// </summary>  
		/// /// <typeparam name="T">The type of the property that has changed.</typeparam>   
		/// /// <param name="propertyName">The name of the property that has changed.</param>     
		/// /// <param name="propertyValue">The original value of the property that has changed.</param> 
		/// /// <param name="value">The new value of the property that has changed.</param>    
		/// /// <returns><c>True</c> if the property was update; otherwise <c>false</c>.</returns>       
		protected virtual bool UpdateProperty<T>(string propertyName, ref T propertyValue, T value)
		{
			if (true == EqualityComparer<T>.Default.Equals(propertyValue, value))
			{
				return false;
			}
			propertyValue = value;
			this.OnPropertyChanged(propertyName);
			return true;
		}
	}

	/// <summary>
	/// CancelableAsyncResult async cancelling of operation
	/// </summary>
	public sealed class CancelableAsyncResult : IAsyncResult
	{
		private IAsyncResult _result;

		private ManualResetEvent _cancelSignal;
		private bool _canceling;

		internal IAsyncResult Result
		{
			get { return _result; }
			set { _result = value; }
		}

		internal ManualResetEvent CancelSignal
		{
			get { return _cancelSignal; }
		}

		internal bool Canceling
		{
			get { return _canceling; }
		}

		public CancelableAsyncResult()
		{
			_cancelSignal = new ManualResetEvent(false);
		}

		~CancelableAsyncResult()
		{
			_cancelSignal.Close();
		}

		public void Cancel()
		{
			_canceling = true;
			_cancelSignal.Set();
		}

		public object AsyncState
		{
			get { return _result.AsyncState; }
		}

		public WaitHandle AsyncWaitHandle
		{
			get { return _result.AsyncWaitHandle; }
		}

		public bool CompletedSynchronously
		{
			get { return _result.CompletedSynchronously; }
		}

		public bool IsCompleted
		{
			get { return _result.IsCompleted; }
		}
	}

	/// <summary>
	/// Signal wich represent lock state
	/// </summary>
	public sealed class LockSignal
	{
		private bool _active;

		public bool Active
		{
			get { return _active; }
			set { _active = value; }
		}

		public LockSignal()
		{
			_active = false;
		}
	}

	public enum AsynchronousState
	{
		CompletedSychronously = 0,
		CompletedAsynchronously,
		Pending
	}

	/// <summary>
	/// Async result based on IAsyncResult
	/// </summary>
	/// <typeparam name="TArg"></typeparam>
	/// <typeparam name="TResult"></typeparam>
	public class AsynchResult<TArg, TResult> : IAsyncResult
	{
		private readonly AsyncCallback m_asynchCallBack;
		private readonly Object m_asychState;
		private int m_completedState;

		private ManualResetEvent m_asynchWaitHandle;
		private Exception m_exception;

		private TResult m_result = default(TResult);
		private TArg m_arg = default(TArg);

		public TArg Arg
		{
			get
			{
				return m_arg;
			}
		}

		public AsynchResult(TArg arg, AsyncCallback callback, Object state)
		{
			m_arg = arg;
			m_asynchCallBack = callback;
			m_asychState = state;
		}

		public void SetAsCompleted(TResult result, bool completedSynchronously, Exception exception)
		{
			m_result = result;
			m_exception = exception;
			int previous = Interlocked.Exchange(ref m_completedState, 
					completedSynchronously ? (int)AsynchronousState.CompletedSychronously : (int)AsynchronousState.CompletedAsynchronously);
			if (previous != (int)AsynchronousState.Pending)
			{
				throw new InvalidOperationException("A result has already been set");
			}
			if (m_asynchWaitHandle != null)
			{
				m_asynchWaitHandle.Set();
			}
			if (m_asynchCallBack != null)
			{
				m_asynchCallBack(this);
			}
		}

		public TResult EndInvoke()
		{
			if (!IsCompleted)
			{
				AsyncWaitHandle.WaitOne();
				AsyncWaitHandle.Close();
				m_asynchWaitHandle = null;
			}
			if (m_exception != null)
			{
				throw m_exception;
			}
			return m_result;
		}

		#region IAsyncResult Members
		public object AsyncState
		{
			get
			{
				return m_asychState;
			}
		}

		public System.Threading.WaitHandle AsyncWaitHandle
		{
			get
			{
				if (m_asynchWaitHandle == null)
				{
					bool done = IsCompleted;
					ManualResetEvent resetEvent = new ManualResetEvent(done);
					if (Interlocked.CompareExchange(ref m_asynchWaitHandle, resetEvent, null) != null)
					{
						resetEvent.Close();
					}
					else
					{
						if (!done && IsCompleted)
						{
							m_asynchWaitHandle.Set();
						}
					}
				} 
				return m_asynchWaitHandle;
			}
		}

		public bool CompletedSynchronously
		{
			get
			{
				return Thread.VolatileRead(ref m_completedState) == (int)AsynchronousState.CompletedSychronously;
			}
		}

		public bool IsCompleted
		{
			get
			{
				return Thread.VolatileRead(ref m_completedState) != (int)AsynchronousState.Pending;
			}
		}

		#endregion
	}

	namespace ContractExt
	{
		using System.Diagnostics;

		public static class Contract
		{
			[DebuggerStepThrough]
			public static void Requires(bool truth, string message = null)
			{
				Requires<Exception>(truth, message);
			}

			[DebuggerStepThrough]
			public static void Requires<TException>(bool truth, string message = null) where TException : Exception
			{
				if (!truth)
				{
					throw InstanceFactory.CreateInstance<TException>(message);
				}
			}

			[DebuggerStepThrough]
			public static void Requires<TException>(bool truth) where TException : Exception, new()
			{
				if (!truth)
				{
					throw new TException();
				}
			}

			public static void Assume(bool truth, string message = null)
			{
				Debug.Assert(truth, message);
			}

			[Conditional("NEVER")]
			public static void Ensures(bool truth)
			{
			}

			[Conditional("NEVER")]
			public static void Invariant(bool truth, string message = null)
			{
				throw new NotSupportedException();
			}

			public static T Result<T>()
			{
				throw new NotSupportedException();
			}

			public static bool ForAll<T>(IEnumerable<T> source, Func<T, bool> predicate)
			{
				return source.All(predicate);
			}
		}

		public class PureAttribute : Attribute { }
		public class ContractClassAttribute : Attribute
		{
			public ContractClassAttribute(Type contractType)
			{
			}
		}

		public class ContractClassForAttribute : Attribute
		{
			public ContractClassForAttribute(Type contractForType)
			{
			}
		}
		public class ContractInvariantMethodAttribute : Attribute
		{
		}
	}

	namespace FileExt
	{
		using System.Drawing;
		using System.Management;
		using System.Xml;

		public static class FileLenghtUtils
		{
			public static double ConvertBytesToMegabytes(long bytes)
			{
				return (bytes / 1024f) / 1024f;
			}

			public static long ConvertBytesToKilobytes(long bytes)
			{
				return (bytes / 1024);
			}

			public static double ConvertKilobytesToMegabytes(long kilobytes)
			{
				return kilobytes / 1024f;
			}
		}

		public static class FileHelper
		{
			/// <summary>
			/// Convert string to byte array (Utf-8)
			/// </summary>
			/// <param name="data"></param>
			/// <returns></returns>
			public static byte[] ConvertStringToByteArray(string data)
			{
				return (new UTF8Encoding()).GetBytes(data);
			}


			/// <summary>
			/// Compute file hash-code representation
			/// </summary>
			/// <param name="fileName"></param>
			/// <returns>hash-code</returns>
			public static string ComputeFileHash(string fileName)
			{
				if (!File.Exists(fileName))
					return string.Empty;

				using (HashAlgorithm hashAlg = HashAlgorithm.Create())
				{
					using (FileStream fs = new FileStream(fileName, FileMode.Open))
					{
						byte[] data = hashAlg.ComputeHash(fs);
						return BitConverter.ToString(data).Replace("-", "");
					}
				}
			}

			/// <summary>
			/// Compute file MD5 hash-code representation
			/// </summary>
			/// <param name="fileName"></param>
			/// <returns>hash-code</returns>
			public static string ComputeMD5FileHash(string fileName)
			{
				if (!File.Exists(fileName))
					return string.Empty;

				using (FileStream fs = new FileStream(fileName, FileMode.Open))
				{
					using (MD5 md5 = new MD5CryptoServiceProvider())
					{
						byte[] data = md5.ComputeHash(fs);
						StringBuilder sb = new StringBuilder();

						for (int i = 0; i < data.Length; i++)
						{
							sb.Append(data[i].ToString("x2"));
						}
						return sb.ToString();
					}
				}
			}

			/// <summary>
			/// Compute file SHA1 hash-code representation
			/// </summary>
			/// <param name="fileName"></param>
			/// <returns>hash-code</returns>
			public static string ComputeSha1FileHash(string fileName)
			{
				if (!File.Exists(fileName))
					return string.Empty;

				using (FileStream fs = new FileStream(fileName, FileMode.Open))
				{
					using (SHA1 sha1 = new SHA1CryptoServiceProvider())
					{
						byte[] data = sha1.ComputeHash(fs);
						return BitConverter.ToString(data).Replace("-", "");
					}
				}
			}

			/// <summary>
			/// Compute file SHA1 hash-code representation
			/// </summary>
			/// <param name="fileName"></param>
			/// <returns>hash-code</returns>
			public static string ComputeSha1FileHashLowered(string fileName)
			{
				if (!File.Exists(fileName))
					return string.Empty;

				using (FileStream fs = new FileStream(fileName, FileMode.Open))
				{
					using (SHA1 sha1 = new SHA1CryptoServiceProvider())
					{
						byte[] data = sha1.ComputeHash(fs);

						StringBuilder sb = new StringBuilder();
						for (int i = 0; i < data.Length; i++)
						{
							sb.Append(data[i].ToString("x2"));
						}
						return sb.ToString();
					}
				}
			}

			/// <summary>
			/// Compare 2 files using MD5 Hash algorithm
			/// </summary>
			/// <param name="file1"></param>
			/// <param name="file2"></param>
			/// <exception cref="System.FileLoadException"/>
			/// <returns>true if equals</returns>
			public static bool AreFilesEquals(string file1, string file2)
			{
				string result = string.Empty;
				if (!File.Exists(file1) || !File.Exists(file1) || string.Compare(file1, file2, StringComparison.OrdinalIgnoreCase) == 0)
					throw new FileLoadException("Files not found \r\n Files cannot  be the same");

				using (HashAlgorithm hashAlg = HashAlgorithm.Create("MD5"))
				{
					using (FileStream fs = new FileStream(file1, FileMode.Open),
						fs2 = new FileStream(file2, FileMode.Open))
					{
						byte[] data = hashAlg.ComputeHash(fs);
						byte[] data2 = hashAlg.ComputeHash(fs2);
						return (BitConverter.ToString(data).Replace("-", "") == BitConverter.ToString(data2).Replace("-", ""));
					}
				}
			}

			public static bool ValidateHash(byte[] key, string message, string delimeter, Encoding encoding)
			{
				using (HMACMD5 hmacMD5 = new HMACMD5(key))
				{
					string[] messageParts = message.Split(new string[] { delimeter }, StringSplitOptions.RemoveEmptyEntries);
					if (messageParts == null || messageParts.Length < 2)
					{
						return false;
					}
					string encodedText = messageParts[0];
					string text = messageParts[1];

					byte[] textBytes = encoding.GetBytes(text);
					byte[] computedHash = hmacMD5.ComputeHash(textBytes);

					string computedHashString = Convert.ToBase64String(computedHash);
					return encodedText.Equals(computedHashString);
				}
			}


			public static string EncodeText(byte[] key, string text, Encoding encoding)
			{
				using (HMACMD5 hmacMD5 = new HMACMD5(key))
				{
					byte[] textBytes = encoding.GetBytes(text);
					byte[] encodedTextBytes = hmacMD5.ComputeHash(textBytes);
					string encodedText = Convert.ToBase64String(encodedTextBytes);
					return encodedText;
				}
			}
		}

		public static class HMACMD5FileEncodeManage
		{
			// Computes a keyed hash for a source file, creates a target file with the keyed hash
			// prepended to the contents of the source file, then decrypts the file and compares
			// the source and the decrypted files.
			public static void EncodeFile(byte[] key, String sourceFile, String destFile)
			{
				// Initialize the keyed hash object.
				HMACMD5 myhmacMD5 = new HMACMD5(key);
				FileStream inStream = new FileStream(sourceFile, FileMode.Open);
				FileStream outStream = new FileStream(destFile, FileMode.Create);
				// Compute the hash of the input file.
				byte[] hashValue = myhmacMD5.ComputeHash(inStream);
				// Reset inStream to the beginning of the file.
				inStream.Position = 0;
				// Write the computed hash value to the output file.
				outStream.Write(hashValue, 0, hashValue.Length);
				// Copy the contents of the sourceFile to the destFile.
				int bytesRead;
				// read 1K at a time
				byte[] buffer = new byte[1024];
				do
				{
					// Read from the wrapping CryptoStream.
					bytesRead = inStream.Read(buffer, 0, 1024);
					outStream.Write(buffer, 0, bytesRead);
				} while (bytesRead > 0);
				myhmacMD5.Clear();
				// Close the streams
				inStream.Close();
				outStream.Close();
				return;
			}

			// Decrypt the encoded file and compare to original file.
			public static bool DecodeFile(byte[] key, String sourceFile)
			{
				// Initialize the keyed hash object. 
				HMACMD5 hmacMD5 = new HMACMD5(key);
				// Create an array to hold the keyed hash value read from the file.
				byte[] storedHash = new byte[hmacMD5.HashSize / 8];
				// Create a FileStream for the source file.
				FileStream inStream = new FileStream(sourceFile, FileMode.Open);
				// Read in the storedHash.
				inStream.Read(storedHash, 0, storedHash.Length);
				// Compute the hash of the remaining contents of the file.
				// The stream is properly positioned at the beginning of the content, 
				// immediately after the stored hash value.
				byte[] computedHash = hmacMD5.ComputeHash(inStream);
				// compare the computed hash with the stored value
				bool err = false;
				for (int i = 0; i < storedHash.Length; i++)
				{
					if (computedHash[i] != storedHash[i])
					{
						err = true;
					}
				}
				if (err)
				{
					Console.WriteLine("Hash values differ! Encoded file has been tampered with!");
					return false;
				}
				else
				{
					Console.WriteLine("Hash values agree -- no tampering occurred.");
					return true;
				}
			}
		}

		public static class FileEncryptionHelper
		{
			//  Call this function to remove the key from memory after use for security.
			[DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
			public static extern bool ZeroMemory(ref string Destination, int Length);

			// Function to Generate a 64 bits Key.
			public static string GenerateKey()
			{
				// Create an instance of Symetric Algorithm. Key and IV is generated automatically.
				DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
				// Use the Automatically generated key for Encryption. 
				return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
			}

			public static void EncryptFile(string key, string inputFile, string outputFile)
			{
				using (FileStream fsIn = new FileStream(inputFile, FileMode.Open),
					fsOut = new FileStream(outputFile, FileMode.Create))
				{
					DESCryptoServiceProvider Des = new DESCryptoServiceProvider();
					Des.Key = ASCIIEncoding.ASCII.GetBytes(key);
					Des.IV = ASCIIEncoding.ASCII.GetBytes(key);

					ICryptoTransform desencrypt = Des.CreateEncryptor();
					CryptoStream cryptostream = new CryptoStream(fsOut, desencrypt, CryptoStreamMode.Write);

					byte[] bytearrayinput = new byte[fsIn.Length - 1];
					fsIn.Read(bytearrayinput, 0, bytearrayinput.Length);
					cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
				}
			}

			public static void DecryptFile(string sInputFilename, string sOutputFilename, string sKey)
			{
				using (DESCryptoServiceProvider DES = new DESCryptoServiceProvider())
				{
					//A 64 bit key and IV is required for this provider.Set secret key For DES algorithm.
					DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
					//Set initialization vector.
					DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);

					//Create a file stream to read the encrypted file back.
					FileStream fsread = new FileStream(sInputFilename, FileMode.Open, FileAccess.Read);
					//Create a DES decryptor from the DES instance.
					ICryptoTransform desdecrypt = DES.CreateDecryptor();
					//Create crypto stream set to read and do a DES decryption transform on incoming bytes.
					CryptoStream cryptostreamDecr = new CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read);
					//Print the contents of the decrypted file.
					StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
					fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
					fsDecrypted.Flush();
					fsDecrypted.Close();
					fsread.Close();
				}
			}

		}

		public static class FileDeleter
		{
			public static void DeleteFolder(string folder)
			{
				DirectoryInfo di = new DirectoryInfo(folder);
				if (!di.Exists)
					return;
				try
				{
					di.Delete();
				}

				catch
				{
					DirectorySecurity diSec = null;
					NTAccount ntAccount = new NTAccount(Environment.UserDomainName, Environment.UserName);

					foreach (DirectoryInfo addDi in di.GetDirectories())
					{
						diSec = addDi.GetAccessControl();
						var currentDerectoryRule = diSec.GetAccessRules(true, false, typeof(NTAccount));
						diSec.AddAccessRule(new FileSystemAccessRule(ntAccount, FileSystemRights.FullControl, AccessControlType.Allow));
						diSec.SetAccessRule(new FileSystemAccessRule(ntAccount, FileSystemRights.FullControl, AccessControlType.Allow));


						foreach (FileInfo fi in di.EnumerateFiles())
						{
							FileSecurity fs = fi.GetAccessControl();
							var sid = fs.GetOwner(typeof(SecurityIdentifier));

							try
							{
								var currentRules = fs.GetAccessRules(true, false, typeof(NTAccount));
								foreach (var r in currentRules.OfType<FileSystemAccessRule>())
								{
									Console.WriteLine(r.AccessControlType + " " + r.FileSystemRights);
								}
								FileSystemAccessRule newRule = new FileSystemAccessRule(ntAccount, FileSystemRights.FullControl, AccessControlType.Allow);
								fs.AddAccessRule(newRule);
								File.SetAccessControl(fi.FullName, fs);
							}
							catch { }
							finally
							{
								fs = null;
								sid = null; ntAccount = null;
							}
						}
					}
				}

			}
		}

		public static class TripleDesFileEncryption
		{
			/// <summary>
			/// Encrypt file using TripleDESCryptoServiceProvider
			/// </summary>
			/// <param name="fileToEncrypt">Source file</param>
			/// <param name="encryptedFile">Encrypted file</param>
			public static void Encrypt(string fileToEncrypt, string encryptedFile, string encryptedKeyFile = "c:\\encryptedKey.key")
			{
				FileStream fsIn = new FileStream(fileToEncrypt, FileMode.Open);
				FileStream fsOut = new FileStream(encryptedFile, FileMode.Create);

				TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

				CryptoStream cs = new CryptoStream(fsOut, tdes.CreateEncryptor(), CryptoStreamMode.Write);

				if (fsIn.CanSeek)
					fsIn.Seek(0, SeekOrigin.Begin);

				byte[] bytearrayinput = new byte[fsIn.Length - 1];
				fsIn.Read(bytearrayinput, 0, bytearrayinput.Length);
				cs.Write(bytearrayinput, 0, bytearrayinput.Length);
				cs.Flush();
				cs.Close();
				fsIn.Close();

				FileStream fsKeyOut = File.Create(encryptedKeyFile);

				BinaryWriter bw = new BinaryWriter(fsKeyOut);

				bw.Write(tdes.Key);
				bw.Write(tdes.IV);

				bw.Flush();
				bw.Close();
			}

			/// <summary>
			/// Decrypt encrypted file
			/// </summary>
			/// <param name="encryptedFile">encrypted file</param>
			public static void Decrypt(string encryptedFile, string encryptedKeyFile = "c:\\encryptedKey.key")
			{
				TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
				FileStream fsKeyIn = File.OpenRead(encryptedKeyFile);
				BinaryReader br = new BinaryReader(fsKeyIn);
				tdes.Key = br.ReadBytes(24);
				tdes.IV = br.ReadBytes(8);
				br.Close();

				FileStream fsIn = File.OpenRead(encryptedFile);
				CryptoStream cs = new CryptoStream(fsIn, tdes.CreateDecryptor(), CryptoStreamMode.Read);
				StreamReader sr = new StreamReader(cs);
				StreamWriter sw = new StreamWriter("c:\\temp.dat");

				int i = 0;
				while ((i = sr.Read()) != -1)
				{
					sw.Write((char)i);
				}
				sw.Close();
				sr.Close();
			}
		}

		public static class RijndaelFileEncryprion
		{
			public static void Encrypt(string strInputFile, string strOutputFile, byte[] bytKey, byte[] bytIV)
			{
				FileInfo FileO = new FileInfo(strOutputFile);
				if (FileO.Exists)
				{
					FileO.Delete();
				}
				FileInfo FileI = new FileInfo(strInputFile);

				try
				{
					FileStream fsInput;
					FileStream fsOutput;

					//Setup file streams to handle input and output.
					fsInput = new FileStream(strInputFile, FileMode.Open, FileAccess.Read);
					fsOutput = new FileStream(strOutputFile, FileMode.OpenOrCreate, FileAccess.Write);

					//fsOutput.Write(GetHash(this.Password), 0, GetHash(this.Password).Length);
					fsOutput.SetLength(0);
					//make sure fsOutput is empty

					//Declare variables for encrypt/decrypt process.
					byte[] bytBuffer = new byte[4097];
					//holds a block of bytes for processing
					long lngBytesProcessed = 0;
					//running count of bytes processed
					long lngFileLength = fsInput.Length;
					//the input file's length
					int intBytesInCurrentBlock = 0;
					//current bytes being processed
					CryptoStream csCryptoStream = default(CryptoStream);
					//Declare your CryptoServiceProvider.
					RijndaelManaged cspRijndael = new RijndaelManaged();
					//Setup Progress Bar

					csCryptoStream = new CryptoStream(fsOutput, cspRijndael.CreateEncryptor(bytKey, bytIV), CryptoStreamMode.Write);
					//Use While to loop until all of the file is processed.

					while (lngBytesProcessed < lngFileLength)
					{
						//Read file with the input filestream.
						intBytesInCurrentBlock = fsInput.Read(bytBuffer, 0, 4096);
						//Write output file with the cryptostream.
						csCryptoStream.Write(bytBuffer, 0, intBytesInCurrentBlock);
						//Update lngBytesProcessed
						lngBytesProcessed = lngBytesProcessed + Convert.ToInt64(intBytesInCurrentBlock);

					}
					csCryptoStream.Close();
					fsInput.Close();
					fsOutput.Close();
				}
				catch
				{
				}

				FileO = null;

			}
		}

		public static class UnfafeFileDeleter
		{
			public static void DeleteFolder(DirectoryInfo directoryInfo)
			{
				GrantAccess(directoryInfo.FullName);
				try
				{
					foreach (DirectoryInfo d in directoryInfo.GetDirectories())
					{
						DeleteFolder(d);
					}
				}
				catch { }

				try
				{
					foreach (FileInfo f in directoryInfo.GetFiles())
					{
						GrantAccess(f.FullName);
						try
						{
							f.Delete();
							Console.WriteLine("Deleted File {0}", f.FullName);
						}
						catch { }
					}
				}
				catch { }

				try
				{
					directoryInfo.Delete(true);
					Console.WriteLine("Deleted Folder {0}", directoryInfo.FullName);
				}
				catch { }

			}

			private static void GrantAccess(string filepath)
			{
				FileSecurity fs = File.GetAccessControl(filepath);
				var sid = fs.GetOwner(typeof(SecurityIdentifier));
				var ntAccount = new NTAccount(Environment.UserDomainName, Environment.UserName);
				try
				{
					var currentRules = fs.GetAccessRules(true, false, typeof(NTAccount));
					foreach (var r in currentRules.OfType<FileSystemAccessRule>())
					{
						Console.WriteLine(r.AccessControlType + " " + r.FileSystemRights);
					}
					var newRule = new FileSystemAccessRule(ntAccount, FileSystemRights.FullControl,
						AccessControlType.Allow);
					fs.AddAccessRule(newRule);
					File.SetAccessControl(filepath, fs);
				}
				catch { }
				finally
				{
					fs = null;
					sid = null; ntAccount = null;
				}
			}
		}

		public class FileAccessManager
		{
			public static string GetFileAccessRules(string filename)
			{
				StringBuilder sb = new StringBuilder();
				FileSecurity security = File.GetAccessControl(filename);
				AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
				foreach (FileSystemAccessRule ace in acl)
				{
					sb.AppendLine(GetAceInformation(ace));
				}

				return sb.ToString();
			}

			public static string GetDirectoryAccessRules(string folder)
			{
				DirectorySecurity security = null;
				StringBuilder sb = new StringBuilder();
				try
				{
					security = Directory.GetAccessControl(folder);
					AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
					foreach (FileSystemAccessRule ace in acl)
					{
						sb.AppendLine(GetAceInformation(ace));
					}
				}
				catch { }
				finally { security = null; }

				return sb.ToString();
			}

			private static string GetAceInformation(FileSystemAccessRule ace)
			{
				StringBuilder info = new StringBuilder();
				string line = string.Format("Account: {0}",
				   ace.IdentityReference.Value);
				info.AppendLine(line);
				line = string.Format("Type: {0}", ace.AccessControlType);
				info.AppendLine(line);
				line = string.Format("Rights: {0}", ace.FileSystemRights);
				info.AppendLine(line);
				line = string.Format("Inherited ACE: {0}", ace.IsInherited);
				info.AppendLine(line);
				return info.ToString();
			}

			public static void AddFullCntrolToFile(string fileName)
			{
				WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
				FileSystemAccessRule rule = new FileSystemAccessRule(self.Name, FileSystemRights.FullControl, AccessControlType.Allow);
				// add the rule to the file's existing ACL list
				FileSecurity security = null;
				try
				{
					security = File.GetAccessControl(fileName);
					AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
					security.AddAccessRule(rule);
					// persist changes
					File.SetAccessControl(fileName, security);
				}
				catch { }
				finally
				{
					security = null;
				}
			}

			public static void AddFullCntrolToFolder(string Folder)
			{
				WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
				FileSystemAccessRule rule = new FileSystemAccessRule(self.Name, FileSystemRights.FullControl, AccessControlType.Allow);
				// add the rule to the file's existing ACL list
				DirectorySecurity security = null;
				try
				{
					security = Directory.GetAccessControl(Folder);
					AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
					security.AddAccessRule(rule);
					// persist changes
					Directory.SetAccessControl(Folder, security);
				}
				catch { }
				finally { security = null; }
			}

			public static void AddFullCntrolToFileForUser(string fileName, NTAccount account)
			{
				if (!File.Exists(fileName) || account == null)
					return;

				FileSystemAccessRule rule = new FileSystemAccessRule(account.Value, FileSystemRights.FullControl, AccessControlType.Allow);
				// add the rule to the file's existing ACL list
				FileSecurity security = File.GetAccessControl(fileName);
				AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
				security.AddAccessRule(rule);
				// persist changes
				File.SetAccessControl(fileName, security);
			}

			public static void AddFullCntrolToFolderForUser(string folder, NTAccount account)
			{
				if (!Directory.Exists(folder) || account == null)
					return;

				FileSystemAccessRule rule = new FileSystemAccessRule(account.Value, FileSystemRights.FullControl, AccessControlType.Allow);
				// add the rule to the file's existing ACL list
				DirectorySecurity security = Directory.GetAccessControl(folder);
				AuthorizationRuleCollection acl = security.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
				security.AddAccessRule(rule);
				// persist changes
				Directory.SetAccessControl(folder, security);
			}

			public static bool CheckIfFolderWritable(string folderPath)
			{
				bool isWriteAccess = false;
				DirectorySecurity diSec = null;
				try
				{
					diSec = Directory.GetAccessControl(folderPath);

					AuthorizationRuleCollection collection = diSec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
					foreach (FileSystemAccessRule rule in collection)
					{
						if (rule.AccessControlType == AccessControlType.Allow)
						{
							isWriteAccess = true;
							break;
						}
					}
				}
				catch (UnauthorizedAccessException) { isWriteAccess = false; }
				catch (Exception) { isWriteAccess = false; }
				finally
				{
					diSec = null;
				}

				return isWriteAccess;
			}

			/// <summary>
			/// Share folder (if folder doesnt exist it will be created)
			/// </summary>
			/// <param name="FolderPath">path to folder</param>
			/// <param name="ShareName"></param>
			/// <param name="Description"></param>
			/// <param name="maxConnections">if dont specify it would be maximum</param>
			/// <returns>true if success</returns>
			public static bool ShareFolder(string FolderPath, string ShareName, string Description, int maxConnections = 0)
			{
				try
				{
					if (!Directory.Exists(FolderPath))
						Directory.CreateDirectory(FolderPath);

					ManagementClass managementClass = new ManagementClass("Win32_Share");
					// Create ManagementBaseObjects for in and out parameters
					ManagementBaseObject inParams = managementClass.GetMethodParameters("Create");
					ManagementBaseObject outParams;
					// Set the input parameters
					inParams["Description"] = Description;
					inParams["Name"] = ShareName;
					inParams["Path"] = FolderPath;
					inParams["Type"] = 0x0; // Disk Drive
					if (maxConnections > 0)
						inParams["MaximumAllowed"] = maxConnections;

					//Another Type:
					// DISK_DRIVE = 0x0  // PRINT_QUEUE = 0x1  // DEVICE = 0x2  // IPC = 0x3
					// DISK_DRIVE_ADMIN = 0x80000000 // PRINT_QUEUE_ADMIN = 0x80000001
					// DEVICE_ADMIN = 0x80000002 // IPC_ADMIN = 0x8000003



					outParams = managementClass.InvokeMethod("Create", inParams, null);
					if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
					{
						return false;
					}
					return true;
				}
				catch (Exception)
				{ }
				return false;
			}
		}

		public enum IconSize
		{
			Large,
			Small
		}

		public class FileIconHelper
		{
			[StructLayout(LayoutKind.Sequential)]
			struct SHFILEINFO
			{
				public IntPtr hIcon;
				public int iIcon;
				public int dwAttributes;
				public string szDisplayName;
				public string szTypeName;
			}

			private const uint SHGFI_SYSICONINDEX = 0x4000;
			private const uint SHGFI_ICON = 0x100;
			private const uint SHGFI_LARGEICON = 0x0;
			private const uint SHGFI_SMALLICON = 0x1;
			private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
			private const uint SHGFI_OPENICON = 0x000000002;
			private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;

			[DllImport("Shell32.dll")]
			private static extern int SHGetFileInfo(string path, uint fileAttributes, out SHFILEINFO psfi, uint fileInfo, uint flags);

			/// <summary>
			/// Get icon from file(folder)
			/// </summary>
			/// <param name="path">path to object</param>
			/// <param name="iconSize">Icon size</param>
			/// <returns></returns>
			public static Icon GetIcon(string path, IconSize iconSize)
			{
				SHFILEINFO info = new SHFILEINFO();
				uint size = (iconSize == IconSize.Large) ? SHGFI_LARGEICON : SHGFI_SMALLICON;
				uint flags = SHGFI_SYSICONINDEX | size | SHGFI_ICON;
				int hTcdf = SHGetFileInfo(path, 0, out info, (uint)Marshal.SizeOf(typeof(SHFILEINFO)), flags);
				IntPtr handleIcon = info.hIcon;
				Icon ico = Icon.FromHandle(handleIcon);
				return ico;
			}

			public static Icon GetFolderIcon(IconSize iconSize)
			{
				uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
				//flags += SHGFI_OPENICON;
				flags += (iconSize == IconSize.Large) ? SHGFI_LARGEICON : SHGFI_SMALLICON;
				SHFILEINFO shfi = new SHFILEINFO();
				int hTcdf = SHGetFileInfo(null, FILE_ATTRIBUTE_DIRECTORY, out shfi, (uint)Marshal.SizeOf(shfi), flags);
				Icon ico = Icon.FromHandle(shfi.hIcon);
				return ico;
			}
		}

		public class FolderPair
		{
			private string name;

			public string Name
			{
				get { return name; }
				set { name = value; }
			}

			private string leftFolder;

			public string LeftFolder
			{
				get { return leftFolder; }
				set { leftFolder = value; }
			}

			private string rightFolder;

			public string RightFolder
			{
				get { return rightFolder; }
				set { rightFolder = value; }
			}

			public override string ToString()
			{
				return name;
			}
		}

		class FolderPairsManager
		{
			private FolderPairsManager()
			{
				this.folderPairs = new Dictionary<string, FolderPair>();
			}

			private static FolderPairsManager instance;

			public static FolderPairsManager Instance
			{
				get { return instance ?? (instance = new FolderPairsManager()); }
				set { instance = value; }
			}

			private Dictionary<string, FolderPair> folderPairs;

			public ICollection<FolderPair> FolderPairs
			{
				get { return folderPairs.Values; }
			}

			private bool collectionDirty;

			public bool CollectionDirty
			{
				get { return collectionDirty; }
				set { collectionDirty = value; }
			}

			public bool Contains(string name)
			{
				return folderPairs.ContainsKey(name);
			}

			public void Add(FolderPair pair)
			{
				folderPairs.Add(pair.Name, pair);
				collectionDirty = true;
			}

			public void Remove(string name)
			{
				folderPairs.Remove(name);
				collectionDirty = true;
			}

			public void Load(string path)
			{
				XmlDocument document = new XmlDocument();
				if (!File.Exists(path))
				{
					return;
				}
				document.Load(path);
				XmlElement root = document.DocumentElement;
				foreach (XmlElement element in document.GetElementsByTagName("FolderPair"))
				{
					FolderPair pair = new FolderPair();
					pair.Name = element.GetAttribute("Name");
					pair.LeftFolder = element["LeftFolder"].InnerText;
					pair.RightFolder = element["RightFolder"].InnerText;
					folderPairs.Add(pair.Name, pair);
				}
			}

			public void Save(string path)
			{
				XmlWriterSettings settings = new XmlWriterSettings();
				settings.Indent = true;
				using (XmlWriter writer = XmlWriter.Create(path, settings))
				{
					writer.WriteStartDocument();
					writer.WriteStartElement("FolderPairs");
					foreach (FolderPair pair in folderPairs.Values)
					{
						writer.WriteStartElement("FolderPair");
						writer.WriteAttributeString("Name", pair.Name);
						writer.WriteElementString("LeftFolder", pair.LeftFolder);
						writer.WriteElementString("RightFolder", pair.RightFolder);
						writer.WriteEndElement();
					}
					writer.WriteFullEndElement();
				}
			}
		}
	}

	namespace Json
	{
		public static class JsonHelper
		{
			public static string Serialize<T>(this object obj)
			{
				using (MemoryStream ms = new MemoryStream())
				{
					DataContractJsonSerializer ser = new DataContractJsonSerializer(obj.GetType());
					ser.WriteObject(ms, obj);
					return Encoding.UTF8.GetString(ms.ToArray());
				}
			}

			public static T Deserialize<T>(string jsonData)
			{
				T obj = Activator.CreateInstance<T>();

				byte[] arrData = Encoding.UTF8.GetBytes(jsonData);
				using (MemoryStream ms = new MemoryStream(arrData))
				{
					DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
					obj = (T)ser.ReadObject(ms);
					return obj;
				}
			}
		}
	}

	namespace RegistryWraper
	{
		#region Public Enums
		/// <summary>
		/// Broad categories of system recognized file format types.
		/// </summary>
		public enum PerceivedTypes
		{
			/// <summary>
			/// No 
			/// </summary>
			None,
			/// <summary>
			/// Image file
			/// </summary>
			Image,
			/// <summary>
			/// Text file
			/// </summary>
			Text,
			/// <summary>
			/// Audio file
			/// </summary>
			Audio,
			/// <summary>
			/// Video file
			/// </summary>
			Video,
			/// <summary>
			/// Compressed file
			/// </summary>
			Compressed,
			/// <summary>
			/// System file
			/// </summary>
			System,
		}
		#endregion

		/// <summary>
		/// Provides a streamlined interface for reading and writing to the registry.
		/// </summary>
		public class RegistryWrapper
		{
			/// <summary>
			/// Reads specified value from the registry.
			/// </summary>
			/// <param name="path">Full registry key (minus root) that contains value.</param>
			/// <param name="valueName">Name of the value within key that will be read.</param>
			/// <returns>Read value.</returns>
			public object Read(string path, string valueName)
			{
				RegistryKey key = Registry.ClassesRoot;
				string[] parts = path.Split('\\');

				if (parts == null || parts.Length == 0)
				{
					return null;
				}

				for (int x = 0; x < parts.Length; x++)
				{
					key = key.OpenSubKey(parts[x]);

					if (key == null)
						return null;

					if (x == parts.Length - 1)
					{
						return key.GetValue(valueName, null, RegistryValueOptions.DoNotExpandEnvironmentNames);
					}

				}

				return null;
			}

			/// <summary>
			/// Writes specified value to the registry.
			/// </summary>
			/// <param name="path">Full registry key (minus root) that will contain the value.</param>
			/// <param name="valueName">Name of the value within key that will be written.</param>
			/// <param name="value">Value to be written</param>
			public void Write(string path, string valueName, object value)
			{
				RegistryKey key = Registry.ClassesRoot;
				RegistryKey lastKey = key;
				string[] parts = path.Split('\\');

				if (parts == null || parts.Length == 0)
				{
					return;
				}

				for (int x = 0; x < parts.Length; x++)
				{
					key = key.OpenSubKey(parts[x], true);

					if (key == null)
					{
						key = lastKey.CreateSubKey(parts[x]);
					}

					if (x == parts.Length - 1)
					{
						if (value is string)
						{
							key.SetValue(valueName, value.ToString());
						}
						else if (value is uint || value.GetType().IsEnum)
						{
							object o = key.GetValue(valueName, null);

							if (o == null)
							{
								key.SetValue(valueName, value, RegistryValueKind.DWord);
							}
							else
							{
								RegistryValueKind kind = key.GetValueKind(valueName);

								if (kind == RegistryValueKind.DWord)
								{
									key.SetValue(valueName, value, RegistryValueKind.DWord);
								}
								else if (kind == RegistryValueKind.Binary)
								{
									uint num = (uint)value;

									byte[] b = new byte[4];
									b[0] = (byte)((num & 0x000000FF) >> 0);
									b[1] = (byte)((num & 0x0000FF00) >> 1);
									b[2] = (byte)((num & 0x00FF0000) >> 2);
									b[3] = (byte)((num & 0xFF000000) >> 3);


									b[0] = (byte)((num & 0x000000FF) >> 0);
									b[1] = (byte)((num & 0x0000FF00) >> 8);
									b[2] = (byte)((num & 0x00FF0000) >> 16);
									b[3] = (byte)((num & 0xFF000000) >> 24);

									key.SetValue(valueName, b, RegistryValueKind.Binary);
								}
								else if (kind == RegistryValueKind.String)
								{
									key.SetValue(valueName, "x" + ((uint)value).ToString("X8"));
								}

							}

						}
						else if (value is Guid)
						{
							key.SetValue(valueName, ((Guid)value).ToString("B"));
						}

					}

					lastKey = key;
				}

				if (key != null)
					key.Close();
			}

			/// <summary>
			/// Deletes specified value;
			/// </summary>
			/// <param name="path">Full registry key (minus root) that contains the value to be deleted.</param>
			/// <param name="valueName">Name of value to be deleted</param>
			public void Delete(string path, string valueName)
			{
				RegistryKey key = Registry.ClassesRoot;
				string[] parts = path.Split('\\');

				if (parts == null || parts.Length == 0)
				{
					return;
				}

				for (int x = 0; x < parts.Length; x++)
				{
					key = key.OpenSubKey(parts[x], true);

					if (key == null)
						return;

					if (x == parts.Length - 1)
					{
						key.DeleteValue(valueName, false);
					}
				}
			}

		}

		/// <summary>
		/// Provides instance methods for the creation, modification, and deletion of file extension associations in the Windows registry.
		/// </summary>
		public class FileAssociationInfo
		{
			private RegistryWrapper registryWrapper = new RegistryWrapper();

			/// <summary>
			/// Gets array containing known file extensions from HKEY_CLASSES_ROOT.
			/// </summary>
			/// <returns>String array containing extensions.</returns>
			public static string[] GetExtensions()
			{
				RegistryKey root = Registry.ClassesRoot;
				List<string> extensionList = new List<string>();

				string[] subKeys = root.GetSubKeyNames();

				foreach (string subKey in subKeys)
				{
					if (subKey.StartsWith("."))
					{
						extensionList.Add(subKey);
					}
				}
				return extensionList.ToArray(); ;
			}

			private string extension;

			/// <summary>
			/// Gets or sets a value that determines the MIME type of the file.
			/// </summary>
			public string ContentType
			{
				get { return GetContentType(this); }
				set { SetContentType(this, value); }
			}

			/// <summary>
			/// Gets a value indicating whether the extension exists.
			/// </summary>
			public bool Exists
			{
				get
				{
					RegistryKey root = Registry.ClassesRoot;
					try
					{
						RegistryKey key = root.OpenSubKey(extension);

						if (key == null)
							return false;

					}
					catch (Exception ex)
					{
						Console.WriteLine(ex.ToString());

						return false;
					}

					return true;
				}
			}

			/// <summary>
			/// Gets the name of the extension.
			/// </summary>
			public string Extension
			{
				get { return extension; }
				set { extension = value; }
			}

			/// <summary>
			/// Gets or sets array of containing program file names which should be displayed in the Open With List.
			/// </summary>
			/// <example>notepad.exe, wordpad.exe, othertexteditor.exe</example>
			public string[] OpenWithList
			{
				get { return GetOpenWithList(this); }
				set { SetOpenWithList(this, value); }
			}

			/// <summary>
			/// Gets or sets a value that determines the <see cref="PerceivedType"/> of the file.
			/// </summary>
			public PerceivedTypes PerceivedType
			{
				get { return GetPerceivedType(this); }
				set { SetPerceivedType(this, value); }
			}

			/// <summary>
			/// Gets or sets a value that indicates the filter component that is used to search for text within documents of this type.
			/// </summary>
			public Guid PersistentHandler
			{
				get { return GetPersistentHandler(this); }
				set { SetPersistentHandler(this, value); }
			}

			/// <summary>
			/// Gets or set a value that indicates the name of the associated application with the behavior to handle this extension.
			/// </summary>
			[XmlAttribute()]
			public string ProgID
			{
				get { return GetProgID(this); }
				set { SetProgID(this, value); }
			}

			/// <summary>
			/// Creates the extension key.
			/// </summary>
			public void Create()
			{
				Create(this);
			}

			/// <summary>
			/// Deletes the extension key.
			/// </summary>
			public void Delete()
			{
				Delete(this);
			}

			/// <summary>
			/// Verifies that given extension exists and is associated with given program id
			/// </summary>
			/// <param name="extension">Extension to be checked for.</param>
			/// <param name="progId">progId to be checked for.</param>
			/// <returns>True if association exists, false if it does not.</returns>
			public bool IsValid(string extension, string progId)
			{
				FileAssociationInfo fai = new FileAssociationInfo(extension);

				if (!fai.Exists)
					return false;

				if (progId != fai.ProgID)
					return false;

				return true;
			}

			/// <summary>
			/// Initializes a new instance of the <see cref="FileAssociationInfo"/>FileAssociationInfo class, which acts as a wrapper for a file extension within the registry.
			/// </summary>
			/// <param name="extension">The dot prefixed extension.</param>
			/// <example>FileAssociationInfo(".mp3")
			/// FileAssociationInfo(".txt")
			/// FileAssociationInfo(".doc")</example>
			public FileAssociationInfo(string extension)
			{
				this.extension = extension;
			}

			#region Public Functions - Creators

			/// <summary>
			/// Creates actual extension association key in registry for the specified extension and supplied attributes.
			/// </summary>
			/// <param name="progId">Name of expected handling program.</param>
			/// <returns>FileAssociationInfo instance referring to specified extension.</returns>
			public FileAssociationInfo Create(string progId)
			{
				return Create(progId, PerceivedTypes.None, string.Empty, null);
			}

			/// <summary>
			/// Creates actual extension association key in registry for the specified extension and supplied attributes.
			/// </summary>
			/// <param name="progId">Name of expected handling program.</param>
			/// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
			/// <returns>FileAssociationInfo instance referring to specified extension.</returns>
			public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType)
			{
				return Create(progId, perceivedType, string.Empty, null);
			}

			/// <summary>
			/// Creates actual extension association key in registry for the specified extension and supplied attributes.
			/// </summary>
			/// <param name="progId">Name of expected handling program.</param>
			/// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
			/// <param name="contentType">MIME type of file type.</param>
			/// <returns>FileAssociationInfo instance referring to specified extension.</returns>
			public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType, string contentType)
			{
				return Create(progId, PerceivedTypes.None, contentType, null);
			}

			/// <summary>
			/// Creates actual extension association key in registry for the specified extension and supplied attributes.
			/// </summary>
			/// <param name="progId">Name of expected handling program.</param>
			/// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
			/// <param name="contentType">MIME type of file type.</param>
			/// <param name="openwithList"></param>
			/// <returns>FileAssociationInfo instance referring to specified extension.</returns>
			public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType, string contentType, string[] openwithList)
			{
				FileAssociationInfo fai = new FileAssociationInfo(extension);

				if (fai.Exists)
				{
					fai.Delete();
				}

				fai.Create();
				fai.ProgID = progId;

				if (perceivedType != PerceivedTypes.None)
					fai.PerceivedType = perceivedType;

				if (contentType != string.Empty)
					fai.ContentType = contentType;

				if (openwithList != null)
					fai.OpenWithList = openwithList;

				return fai;
			}

			#endregion

			#region Private Functions - Property backend

			/// <summary>
			/// Gets array of containing program file names which should be displayed in the Open With List.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <returns>Program file names</returns>
			protected string[] GetOpenWithList(FileAssociationInfo file)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				RegistryKey root = Registry.ClassesRoot;

				RegistryKey key = root.OpenSubKey(file.extension);

				key = key.OpenSubKey("OpenWithList");

				if (key == null)
				{
					return new string[0];
				}

				return key.GetSubKeyNames();

			}

			/// <summary>
			/// Sets array of containing program file names which should be displayed in the Open With List.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <param name="programList">Program file names</param>
			protected void SetOpenWithList(FileAssociationInfo file, string[] programList)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				RegistryKey root = Registry.ClassesRoot;

				RegistryKey key = root.OpenSubKey(file.extension, true);

				RegistryKey tmpkey = key.OpenSubKey("OpenWithList", true);

				if (tmpkey != null)
				{
					key.DeleteSubKeyTree("OpenWithList");
				}

				key = key.CreateSubKey("OpenWithList");

				foreach (string s in programList)
				{
					key.CreateSubKey(s);
				}
			}

			/// <summary>
			/// Gets or value that determines the <see cref="PerceivedType"/>PerceivedType of the file.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <returns><see cref="PerceivedTypes"/> that specifies Perceived Type of extension.</returns>
			protected PerceivedTypes GetPerceivedType(FileAssociationInfo file)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				object val = registryWrapper.Read(file.extension, "PerceivedType");
				PerceivedTypes actualType = PerceivedTypes.None;

				if (val == null)
					return actualType;

				try
				{
					actualType = (PerceivedTypes)Enum.Parse(typeof(PerceivedTypes), val.ToString(), true);
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex.ToString());
				}


				return actualType;
			}

			/// <summary>
			/// Sets a value that determines the <see cref="PerceivedType"/>PerceivedType of the file.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <param name="type"><see cref="PerceivedTypes"/> to be set that specifies Perceived Type of extension.</param>
			protected void SetPerceivedType(FileAssociationInfo file, PerceivedTypes type)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				registryWrapper.Write(file.extension, "PerceivedType", type.ToString());

			}


			/// <summary>
			/// Gets a value that indicates the filter component that is used to search for text within documents of this type.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <returns>Guid of filter component.</returns>
			protected Guid GetPersistentHandler(FileAssociationInfo file)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				object val = registryWrapper.Read(file.extension + "\\PersistentHandler", string.Empty);

				if (val == null)
					return new Guid();
				else
					return new Guid(val.ToString());
			}

			/// <summary>
			/// Sets a value that indicates the filter component that is used to search for text within documents of this type.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <param name="persistentHandler">Guid of filter component.</param>
			protected void SetPersistentHandler(FileAssociationInfo file, Guid persistentHandler)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				if (persistentHandler == Guid.Empty)
					return;

				this.registryWrapper.Write(file.extension + "\\" + PersistentHandler, string.Empty, persistentHandler);

			}

			/// <summary>
			/// Gets a value that determines the MIME type of the file.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <returns>MIME content type of extension.</returns>
			protected string GetContentType(FileAssociationInfo file)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				object val = registryWrapper.Read(file.extension, "Content Type");

				if (val == null)
				{
					return string.Empty;
				}
				else
				{
					return val.ToString();
				}
			}

			/// <summary>
			/// Sets a value that determines the MIME type of the file.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <param name="type">MIME content type of extension.</param>
			protected void SetContentType(FileAssociationInfo file, string type)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				registryWrapper.Write(file.extension, "Content Type", type);

				ShellNotification.NotifyOfChange();
			}


			/// <summary>
			/// Gets a value that indicates the name of the associated application with the behavior to handle this extension.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <returns>Associated Program ID of handling program.</returns>
			protected string GetProgID(FileAssociationInfo file)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				object val = registryWrapper.Read(file.extension, string.Empty);

				if (val == null)
					return string.Empty;

				return val.ToString();
			}

			/// <summary>
			/// Set a value that indicates the name of the associated application with the behavior to handle this extension.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
			/// <param name="progId">Associated Program ID of handling program.</param>
			protected void SetProgID(FileAssociationInfo file, string progId)
			{
				if (!file.Exists)
					throw new Exception("Extension does not exist");

				registryWrapper.Write(file.extension, string.Empty, progId);

				ShellNotification.NotifyOfChange();
			}

			#endregion

			/// <summary>
			/// Creates actual file extension entry in registry.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> instance that contains specifics on extension to be created.</param>
			protected void Create(FileAssociationInfo file)
			{
				if (file.Exists)
				{
					file.Delete();
				}

				RegistryKey root = Registry.ClassesRoot;

				root.CreateSubKey(file.extension);
			}

			/// <summary>
			/// Deletes actual file extension entry in registry.
			/// </summary>
			/// <param name="file"><see cref="FileAssociationInfo"/> instance that contains specifics on extension to be deleted.</param>
			protected void Delete(FileAssociationInfo file)
			{
				if (!file.Exists)
				{
					throw new Exception("Key not found.");
				}

				RegistryKey root = Registry.ClassesRoot;

				root.DeleteSubKeyTree(file.extension);
			}
		}


		/// <summary>
		/// Provides more streamlined interface for associating a single or multiple extensions with a single program.
		/// </summary>
		class AssociationManager
		{
			/// <summary>
			/// Determines of the list of extensions are associated with the specified program id.
			/// </summary>
			/// <param name="progId">Program id to check against.</param>
			/// <param name="extensions">String array of extensions to check against the program id.</param>
			/// <returns>String array of extensions that were not associated with the program id.</returns>
			public string[] CheckAssociation(string progId, params string[] extensions)
			{
				List<string> notAssociated = new List<string>();

				foreach (string s in extensions)
				{
					FileAssociationInfo fai = new FileAssociationInfo(s);

					if (!fai.Exists || fai.ProgID != progId)
						notAssociated.Add(s);
				}

				return notAssociated.ToArray();

			}

			/// <summary>
			/// Associates a single executable with a list of extensions.
			/// </summary>
			/// <param name="progId">Name of program id</param>
			/// <param name="executablePath">Path to executable to start including arguments.</param>
			/// <param name="extensions">String array of extensions to associate with program id.</param>
			/// <example>progId = "MyTextFile"
			/// executablePath = "notepad.exe %1"
			/// extensions = ".txt", ".text"</example>
			public void Associate(string progId, string executablePath, params string[] extensions)
			{
				foreach (string s in extensions)
				{
					FileAssociationInfo fai = new FileAssociationInfo(s);

					if (!fai.Exists)
						fai.Create(progId);

					fai.ProgID = progId;
				}
			}

			/// <summary>
			/// Associates an already existing program id with a list of extensions.
			/// </summary>
			/// <param name="progId">The program id to associate extensions with.</param>
			/// <param name="extensions">String array of extensions to associate with program id.</param>
			public void Associate(string progId, params string[] extensions)
			{
				foreach (string s in extensions)
				{
					FileAssociationInfo fai = new FileAssociationInfo(s);

					if (!fai.Exists)
						fai.Create(progId);

					fai.ProgID = progId;
				}
			}

		}
	}

	namespace ShellExt
	{
		using System.Management;
		using System.Threading;

		public class ShellNotification
		{
			/// <summary>
			/// Notifies the system of an event that an application has performed. An application should use this function if it performs an action that may affect the Shell. 
			/// </summary>
			/// <param name="wEventId">Describes the event that has occurred. The ShellChangeNotificationEvents enum contains a list of options.</param>
			/// <param name="uFlags">Flags that indicate the meaning of the dwItem1 and dwItem2 parameters.</param>
			/// <param name="dwItem1">First event-dependent value.</param>
			/// <param name="dwItem2">Second event-dependent value.</param>
			[DllImport("shell32.dll")]
			private static extern void SHChangeNotify(UInt32 wEventId, UInt32 uFlags, IntPtr dwItem1, IntPtr dwItem2);

			/// <summary>
			/// Notify shell of change of file associations.
			/// </summary>
			public static void NotifyOfChange()
			{
				SHChangeNotify((uint)ShellChangeNotificationEvents.SHCNE_ASSOCCHANGED,
					(uint)(ShellChangeNotificationFlags.SHCNF_IDLIST | ShellChangeNotificationFlags.SHCNF_FLUSHNOWAIT),
					IntPtr.Zero, IntPtr.Zero);
			}


			[Flags]
			private enum ShellChangeNotificationEvents : uint
			{
				/// <summary>
				/// The name of a nonfolder item has changed. SHCNF_IDLIST or  SHCNF_PATH must be specified in uFlags. dwItem1 contains the  previous PIDL or name of the item. dwItem2 contains the new PIDL or name of the item. 
				/// </summary>
				SHCNE_RENAMEITEM = 0x00000001,
				/// <summary>
				/// A nonfolder item has been created. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the item that was created. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_CREATE = 0x00000002,
				/// <summary>
				/// A nonfolder item has been deleted. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the item that was deleted. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_DELETE = 0x00000004,
				/// <summary>
				/// A folder has been created. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the folder that was created. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_MKDIR = 0x00000008,
				/// <summary>
				/// A folder has been removed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the folder that was removed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_RMDIR = 0x00000010,
				/// <summary>
				/// Storage media has been inserted into a drive. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive that contains the new media. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_MEDIAINSERTED = 0x00000020,
				/// <summary>
				/// Storage media has been removed from a drive. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive from which the media was removed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_MEDIAREMOVED = 0x00000040,
				/// <summary>
				/// A drive has been removed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive that was removed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_DRIVEREMOVED = 0x00000080,
				/// <summary>
				/// A drive has been added. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive that was added. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_DRIVEADD = 0x00000100,
				/// <summary>
				/// A folder on the local computer is being shared via the network. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the folder that is being shared. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_NETSHARE = 0x00000200,
				/// <summary>
				/// A folder on the local computer is no longer being shared via the network. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the folder that is no longer being shared. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_NETUNSHARE = 0x00000400,
				/// <summary>
				/// The attributes of an item or folder have changed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the item or folder that has changed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_ATTRIBUTES = 0x00000800,
				/// <summary>
				/// The contents of an existing folder have changed, but the folder still exists and has not been renamed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the folder that has changed. dwItem2 is not used and should be NULL. If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or SHCNE_RENAMEFOLDER, respectively, instead.
				/// </summary>
				SHCNE_UPDATEDIR = 0x00001000,
				/// <summary>
				/// An existing nonfolder item has changed, but the item still exists and has not been renamed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the item that has changed. dwItem2 is not used and should be NULL. If a nonfolder item has been created, deleted, or renamed, use SHCNE_CREATE, SHCNE_DELETE, or SHCNE_RENAMEITEM, respectively, instead.
				/// </summary>
				SHCNE_UPDATEITEM = 0x00002000,
				/// <summary>
				/// The computer has disconnected from a server. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the server from which the computer was disconnected. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_SERVERDISCONNECT = 0x00004000,
				/// <summary>
				/// An image in the system image list has changed. SHCNF_DWORD must be specified in uFlags. dwItem1 contains the index in the system image list that has changed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_UPDATEIMAGE = 0x00008000,
				/// <summary>
				/// A drive has been added and the Shell should create a new window for the drive. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive that was added. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_DRIVEADDGUI = 0x00010000,
				/// <summary>
				/// The name of a folder has changed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the previous pointer to an item identifier list (PIDL) or name of the folder. dwItem2 contains the new PIDL or name of the folder.
				/// </summary>
				SHCNE_RENAMEFOLDER = 0x00020000,
				/// <summary>
				/// The amount of free space on a drive has changed. SHCNF_IDLIST or SHCNF_PATH must be specified in uFlags. dwItem1 contains the root of the drive on which the free space changed. dwItem2 is not used and should be NULL.
				/// </summary>
				SHCNE_FREESPACE = 0x00040000,
				/// <summary>
				/// Not currently used.
				/// </summary>
				SHCNE_EXTENDED_EVENT = 0x04000000,
				/// <summary>
				/// A file type association has changed. SHCNF_IDLIST must be specified in the uFlags parameter. dwItem1 and dwItem2 are not used and must be NULL.
				/// </summary>
				SHCNE_ASSOCCHANGED = 0x08000000,
				/// <summary>
				/// Specifies a combination of all of the disk event identifiers.
				/// </summary>
				SHCNE_DISKEVENTS = 0x0002381F,
				/// <summary>
				/// Specifies a combination of all of the global event identifiers. 
				/// </summary>
				SHCNE_GLOBALEVENTS = 0x0C0581E0,
				/// <summary>
				/// All events have occurred.
				/// </summary>
				SHCNE_ALLEVENTS = 0x7FFFFFFF,
				/// <summary>
				/// The specified event occurred as a result of a system interrupt. As this value modifies other event values, it cannot be used alone.
				/// </summary>
				SHCNE_INTERRUPT = 0x80000000
			}

			private enum ShellChangeNotificationFlags
			{
				/// <summary>
				/// dwItem1 and dwItem2 are the addresses of ITEMIDLIST structures that represent the item(s) affected by the change. Each ITEMIDLIST must be relative to the desktop folder. 
				/// </summary>
				SHCNF_IDLIST = 0x0000,
				/// <summary>
				/// dwItem1 and dwItem2 are the addresses of null-terminated strings of maximum length MAX_PATH that contain the full path names of the items affected by the change.
				/// </summary>
				SHCNF_PATHA = 0x0001,
				/// <summary>
				/// dwItem1 and dwItem2 are the addresses of null-terminated strings that represent the friendly names of the printer(s) affected by the change.
				/// </summary>
				SHCNF_PRINTERA = 0x0002,
				/// <summary>
				/// The dwItem1 and dwItem2 parameters are DWORD values.
				/// </summary>
				SHCNF_DWORD = 0x0003,
				/// <summary>
				/// like SHCNF_PATHA but unicode string
				/// </summary>
				SHCNF_PATHW = 0x0005,
				/// <summary>
				/// like SHCNF_PRINTERA but unicode string
				/// </summary>
				SHCNF_PRINTERW = 0x0006,
				/// <summary>
				/// 
				/// </summary>
				SHCNF_TYPE = 0x00FF,
				/// <summary>
				/// The function should not return until the notification has been delivered to all affected components. As this flag modifies other data-type flags, it cannot by used by itself.
				/// </summary>
				SHCNF_FLUSH = 0x1000,
				/// <summary>
				/// The function should begin delivering notifications to all affected components but should return as soon as the notification process has begun. As this flag modifies other data-type flags, it cannot by used  by itself.
				/// </summary>
				SHCNF_FLUSHNOWAIT = 0x2000
			}
		}

		public delegate void BetteryStatusHandler(object sender, BetteryStatusEventArgs e);

		public class BetteryInfo : IDisposable
		{
			Dictionary<UInt16, string> StatusCodes;

			private Timer m_timer;

			public event BetteryStatusHandler StatusChanged;

			public BetteryInfo()
			{
				StatusCodes = new Dictionary<ushort, string>();
				StatusCodes.Add(1, "The battery is discharging");
				StatusCodes.Add(2, "The system has access to AC so no battery is being discharged. However, the battery is not necessarily charging");
				StatusCodes.Add(3, "Fully Charged");
				StatusCodes.Add(4, "Low");
				StatusCodes.Add(5, "Critical");
				StatusCodes.Add(6, "Charging");
				StatusCodes.Add(7, "Charging and High");
				StatusCodes.Add(8, "Charging and Low");
				StatusCodes.Add(9, "Undefined");
				StatusCodes.Add(10, "Partially Charged");/* Set progress bar values and Properties */

				Init();
				m_timer = new Timer(new TimerCallback(TimerCallbackHandler), null, 0, 5000);
			}

			public string BetteryName
			{
				get;
				private set;
			}

			public string BetteryStatus
			{
				get;
				private set;
			}

			private void Init()
			{
				ManagementObjectSearcher mos = new ManagementObjectSearcher("select * from Win32_Battery");
				foreach (ManagementObject mo in mos.Get())
				{
					BetteryName = mo["Name"].ToString();
					UInt16 statuscode = (UInt16)mo["BatteryStatus"];
					BetteryStatus = StatusCodes[statuscode];
				}
			}

			private void TimerCallbackHandler(object sender)
			{
				Init();
				BetteryStatusHandler handler = StatusChanged;
				if (handler != null)
				{
					StatusChanged(this, new BetteryStatusEventArgs()
					{
						BetteryName = this.BetteryName,
						BetteryStatus = this.BetteryStatus
					});
				}

			}


			private bool disposed = false;
			public void Dispose()
			{
				if (!disposed)
				{
					StatusCodes.Clear();
					StatusCodes = null;
					m_timer.Dispose();
					BetteryName = null;
					BetteryStatus = null;
					GC.SuppressFinalize(this);
				}
			}
		}

		public class BetteryStatusEventArgs : EventArgs
		{
			public BetteryStatusEventArgs()
				: base()
			{

			}

			public string BetteryName
			{
				get;
				set;
			}

			public string BetteryStatus
			{
				get;
				set;
			}
		}
	}

	namespace TypeHelper
	{
		using System.Reflection;

		public static class ClassHelper
		{
			public static readonly BindingFlags AllFlag = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic;
			public static readonly BindingFlags StaticFlag = BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic;
			public static readonly BindingFlags InstanceFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
			public static readonly BindingFlags InstancePublic = BindingFlags.Public | BindingFlags.Instance;

			public static object CreateInstance(Type t)
			{
				return CreateInstance(t, new object[] { });
			}

			public static object CreateInstance(Type t, params object[] os)
			{
				var ts = GetTypesByObjs(os);
				var ci = t.GetConstructor(InstanceFlag, null, ts, null);
				return ci.Invoke(os);
			}

			public static Type[] GetTypesByObjs(params object[] os)
			{
				var ts = new Type[os.Length];
				for (int i = 0; i < os.Length; i++)
				{
					ts[i] = os[i].GetType();
				}
				return ts;
			}

			public static T CtreateInstance<T>()
			{
				Type t = typeof(T);

				T instObj = (T)Activator.CreateInstance(t);

				return instObj;
			}

			public static T CreateInstanceAndInitializeProperties<T>()
			{
				Type t = typeof(T);

				T instObj = (T)Activator.CreateInstance(t);

				foreach (PropertyInfo pi in t.GetProperties())
				{
					switch (pi.PropertyType.Name.ToLower())
					{
						case "string":
							if (string.IsNullOrEmpty(pi.GetValue(instObj, null) as string))
							{
								pi.SetValue(instObj, string.Empty, null);
							}
							break;

						default:
							break;
					}
				}

				return instObj;

			}

			public static ConstructorInfo GetArgumentlessConstructor(Type t)
			{
				return t.GetConstructor(InstancePublic, null, new Type[] { }, null);
			}

			public static ConstructorInfo[] GetPublicOrProtectedConstructors(Type t)
			{
				ConstructorInfo[] cs = t.GetConstructors(InstanceFlag);
				var list = new List<ConstructorInfo>();
				foreach (ConstructorInfo info in cs)
				{
					if (info.IsPublic || info.IsFamily)
					{
						list.Add(info);
					}
				}
				return list.ToArray();
			}

			public static T[] GetAttributes<T>(this MethodInfo info, bool inherit) where T : Attribute
			{
				var os = info.GetCustomAttributes(typeof(T), inherit);
				return (T[])os;
			}

			public static T GetAttribute<T>(this MethodInfo info, bool inherit) where T : Attribute
			{
				var ts = GetAttributes<T>(info, inherit);
				if (ts != null && ts.Length > 0)
					return ts[0];
				return null;
			}

			public static T[] GetAttributes<T>(this FieldInfo info, bool inherit) where T : Attribute
			{
				var os = info.GetCustomAttributes(typeof(T), inherit);
				return (T[])os;
			}

			public static T GetAttribute<T>(this FieldInfo info, bool inherit) where T : Attribute
			{
				var ts = GetAttributes<T>(info, inherit);
				if (ts != null && ts.Length > 0)
					return ts[0];
				return null;
			}

			public static T[] GetAttributes<T>(this PropertyInfo info, bool inherit) where T : Attribute
			{
				var os = info.GetCustomAttributes(typeof(T), inherit);
				return (T[])os;
			}

			public static T GetAttribute<T>(this PropertyInfo info, bool inherit) where T : Attribute
			{
				var ts = GetAttributes<T>(info, inherit);
				if (ts != null && ts.Length > 0)
					return ts[0];
				return null;
			}

			public static T[] GetAttributes<T>(this Type info, bool inherit) where T : Attribute
			{
				var os = info.GetCustomAttributes(typeof(T), inherit);
				return (T[])os;
			}

			public static T GetAttribute<T>(this Type info, bool inherit) where T : Attribute
			{
				var ts = GetAttributes<T>(info, inherit);
				if (ts != null && ts.Length > 0)
					return ts[0];
				return null;
			}

			public static T[] GetAttributes<T>(this ParameterInfo info, bool inherit) where T : Attribute
			{
				var os = info.GetCustomAttributes(typeof(T), inherit);
				return (T[])os;
			}

			public static T GetAttribute<T>(this ParameterInfo info, bool inherit) where T : Attribute
			{
				var ts = GetAttributes<T>(info, inherit);
				if (ts != null && ts.Length > 0)
					return ts[0];
				return null;
			}

		}
	}

	namespace StringExt
	{
		using System.Text.RegularExpressions;
		using System.Xml.Linq;
		using System.Xml;
		using System.Xml.XPath;

		public static class TextHelper
		{
			public static List<string> SplitByCase(string s)
			{
				var result = new List<string>();
				int n = 0, i;
				for (i = 0; i < s.Length; i++)
				{
					var c = s[i];
					if (c >= 'A' && c <= 'Z')
					{
						if (i > 0)
						{
							result.Add(s.Substring(n, i - n));
							n = i;
						}
					}
				}
				if (i > n)
				{
					result.Add(s.Substring(n, i - n));
				}
				return result;
			}

			public static string EnsureStartsWith(this string value, string prefix)
			{
				return value.StartsWith(prefix) ? value : string.Concat(prefix, value);
			}

			public static string EnsureEndsWith(string s, char c)
			{
				return EnsureEndsWith(s, new string(c, 1));
			}

			public static string EnsureEndsWith(string s, string s1)
			{
				if (!s.EndsWith(s1))
				{
					return s + s1;
				}
				return s;
			}

			public static string MultiLineAddPrefix(string source)
			{
				return MultiLineAddPrefix(source, "\t");
			}

			public static string MultiLineAddPrefix(string source, string prefix)
			{
				return MultiLineAddPrefix(source, prefix, '\n');
			}

			public static string MultiLineAddPrefix(string source, string prefix, char splitBy)
			{
				var sb = new StringBuilder();
				string[] ss = source.Split(splitBy);
				foreach (string s in ss)
				{
					sb.Append(prefix);
					sb.Append(s);
					sb.Append(splitBy);
				}
				return sb.ToString();
			}

			/// <summary>
			/// Get string content that can contains in C++ file(end with '\0')
			/// </summary>
			/// <param name="s"></param>
			/// <returns></returns>
			public static string GetCString(string s)
			{
				int n = s.IndexOf('\0');
				if (n > 0)
				{
					return s.Substring(0, n);
				}
				return s;
			}

			/// <summary>
			/// 	Gets the string before the given string parameter.
			/// </summary>
			/// <param name = "value">The default value.</param>
			/// <param name = "x">The given string parameter.</param>
			/// <returns></returns>
			public static string GetBefore(this string value, string x)
			{
				int position = value.IndexOf(x);
				return position == -1 ? String.Empty : value.Substring(0, position);
			}

			/// <summary>
			/// 	Gets the string after the given string parameter.
			/// </summary>
			/// <param name = "value">The default value.</param>
			/// <param name = "x">The given string parameter.</param>
			/// <returns></returns>
			public static string GetAfter(this string value, string x)
			{
				int position = value.LastIndexOf(x);

				if (position == -1)
					return String.Empty;

				int startIndex = position + x.Length;
				return startIndex >= value.Length ? String.Empty : value.Substring(startIndex).Trim();
			}

			public static string GetPassword()
			{
				StringBuilder builder = new StringBuilder();
				builder.Append(RandomString(4, true));
				builder.Append(RandomNumber(1000, 9999));
				builder.Append(RandomString(2, false));
				return builder.ToString();
			}

			/// <summary>
			/// Create string content that can contains in C++ file(end with '\0')
			/// </summary>
			/// <param name="s"></param>
			/// <returns></returns>
			public static string CreateCppString(string s)
			{
				int n = s.IndexOf('\0');
				if (n < 0)
				{
					StringBuilder sb = new StringBuilder(s);
					sb.Append('\0');
					return sb.ToString();
				}
				return s;
			}

			public static byte[] GetBytesByLength(byte[] bs, int length)
			{
				var ms = new MemoryStream(bs, 0, length);
				return ms.ToArray();
			}

			public static string Capitalize(string s)
			{
				return s[0].ToString().ToUpper() + s.Substring(1);
			}

			/// <summary>
			/// Get hash using SHA512
			/// </summary>
			/// <param name="s"></param>
			/// <returns></returns>
			public static byte[] HashSHA512(string s)
			{
				using (SHA512 sha512 = SHA512.Create())
				{
					byte[] input = Encoding.Unicode.GetBytes(s);
					return sha512.ComputeHash(input);
				}
			}

			/// <summary>
			/// Get has by MD5
			/// </summary>
			/// <param name="s"></param>
			/// <returns></returns>
			public static byte[] HashMd5(string s)
			{
				using (MD5 md5 = MD5.Create())
				{
					byte[] input = Encoding.Unicode.GetBytes(s);
					return md5.ComputeHash(input);
				}
			}

			/// <summary>
			/// Read all text in file in UTF8
			/// </summary>
			/// <param name="fileName"></param>
			/// <returns></returns>
			public static string ReadFileToEnd(string fileName)
			{
				return ReadFileToEnd(fileName, Encoding.UTF8);
			}

			/// <summary>
			/// Read all text content from file
			/// </summary>
			/// <param name="fileName"></param>
			/// <param name="coding"></param>
			/// <returns></returns>
			public static string ReadFileToEnd(string fileName, Encoding coding)
			{
				if (!File.Exists(fileName))
					return string.Empty;

				using (var sr = new StreamReader(fileName, coding))
				{
					return sr.ReadToEnd();
				}
			}

			public static string FixWhiteSpaces(string s)
			{
				return (Regex.Replace(s, " ", "").ToString());
			}

			public static T SafeConvert<T>(string s, T defaultValue)
			{
				if (string.IsNullOrEmpty(s))
					return defaultValue;
				return (T)Convert.ChangeType(s, typeof(T));
			}

			/// <summary>
			/// Generates a random string with the given length
			/// </summary>
			/// <param name="size">Size of the string</param>
			/// <param name="lowerCase">If true, generate lowercase string</param>
			/// <returns>Random string</returns>
			public static string RandomString(int size, bool lowerCase)
			{
				StringBuilder builder = new StringBuilder();
				Random random = new Random();
				char ch;
				for (int i = 0; i < size; i++)
				{
					ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
					builder.Append(ch);
				}
				if (lowerCase)
					return builder.ToString().ToLower();
				return builder.ToString();
			}

			/// <summary>
			/// 	Remove any instance of the given character from the current string.
			/// </summary>
			/// <param name = "value">
			/// 	The input.
			/// </param>
			/// <param name = "removeCharc">
			/// 	The remove char.
			/// </param>
			/// <remarks>
			/// 	Contributed by Michael T, http://about.me/MichaelTran
			/// </remarks>
			public static string Remove(this string value, params char[] removeCharc)
			{
				var result = value;
				if (!string.IsNullOrEmpty(result) && removeCharc != null)
					Array.ForEach(removeCharc, c => result = result.Remove(c.ToString()));

				return result;
			}

			/// <summary>
			/// 	Remove any instance of the given string pattern from the current string.
			/// </summary>
			/// <param name = "value">
			/// 	The input.
			/// </param>
			/// <param name = "removeStrings">
			/// 	The remove Strings.
			/// </param>
			/// <remarks>
			/// 	Contributed by Michael T, http://about.me/MichaelTran
			/// </remarks>
			public static string Remove(this string value, params string[] strings)
			{
				return strings.Aggregate(value, (current, c) => current.Replace(c, string.Empty));
				//var result = value;
				//if (!string.IsNullOrEmpty(result) && removeStrings != null)
				//  Array.ForEach(removeStrings, s => result = result.Replace(s, string.Empty));

				//return result;
			}

			private static int RandomNumber(int min, int max)
			{
				Random random = new Random();
				return random.Next(min, max);
			}

			//public static string Data_Asc_Hex(ref string Data)
			//{
			//    //first take each charcter using substring.
			//    //then convert character into ascii.
			//    //then convert ascii value into Hex Format
			//    string sValue;
			//    string sHex = "";
			//    while (Data.Length > 0)
			//    {
			//        sValue = Conversion.Hex(Strings.Asc(Data.Substring(0, 1).ToString()));
			//        Data = Data.Substring(1, Data.Length - 1);
			//        sHex = sHex + sValue;
			//    }
			//    return sHex;
			//}

			/// <summary>
			/// Read all the lines in the string.
			/// </summary>
			/// <param name="text"></param>
			/// <returns></returns>
			public static IList<string> ReadLines(string text)
			{
				// Check for empty and return empty list.
				if (string.IsNullOrEmpty(text))
					return new List<string>();

				StringReader reader = new StringReader(text);
				string currentLine = reader.ReadLine();
				IList<string> lines = new List<string>();

				// More to read.
				while (currentLine != null)
				{
					lines.Add(currentLine);
					currentLine = reader.ReadLine();
				}
				return lines;
			}

			/// <summary>
			/// Truncates the string.
			/// </summary>
			/// <param name="txt"></param>
			/// <param name="maxChars"></param>
			/// <returns></returns>
			public static string Truncate(string txt, int maxChars)
			{
				if (string.IsNullOrEmpty(txt))
					return txt;

				if (txt.Length <= maxChars)
					return txt;

				return txt.Substring(0, maxChars);
			}

			/// <summary>
			/// Truncate the text supplied by number of characters specified by <paramref name="maxChars"/>
			/// and then appends the suffix.
			/// </summary>
			/// <param name="txt"></param>
			/// <param name="maxChars"></param>
			/// <param name="suffix"></param>
			/// <returns></returns>
			public static string TruncateWithText(string txt, int maxChars, string suffix)
			{
				if (string.IsNullOrEmpty(txt))
					return txt;

				if (txt.Length <= maxChars)
					return txt;

				// Now do the truncate and more.
				string partial = txt.Substring(0, maxChars);
				return partial + suffix;
			}

			/// <summary>
			/// Converts to string.
			/// </summary>
			/// <param name="args">The args.</param>
			/// <returns></returns>
			public static string ConvertToString(object[] args)
			{
				if (args == null || args.Length == 0)
					return string.Empty;

				StringBuilder buffer = new StringBuilder();
				foreach (object arg in args)
				{
					if (arg != null)
						buffer.Append(arg.ToString());
				}
				return buffer.ToString();
			}

			/// <summary>
			/// Checks whether the string is empty and returns a default value in case.
			/// </summary>
			/// <param name = "value">The string to check.</param>
			/// <param name = "defaultValue">The default value.</param>
			/// <returns>Either the string or the default value.</returns>
			public static string IfEmpty(this string value, string defaultValue)
			{
				return (!string.IsNullOrEmpty(value) ? value : defaultValue);
			}

			/// <summary>
			/// 	Loads the string into a LINQ to XML XDocument
			/// </summary>
			/// <param name = "xml">The XML string.</param>
			/// <returns>The XML document object model (XDocument)</returns>
			public static XDocument ToXDocument(this string xml)
			{
				return XDocument.Parse(xml);
			}

			/// <summary>
			/// Loads the string into a XML DOM object (XmlDocument)
			/// </summary>
			/// <param name = "xml">The XML string.</param>
			/// <returns>The XML document object model (XmlDocument)</returns>
			public static XmlDocument ToXmlDOM(this string xml)
			{
				XmlDocument document = new XmlDocument();
				document.LoadXml(xml);
				return document;
			}

			/// <summary>
			/// Loads the string into a XML XPath DOM (XPathDocument)
			/// </summary>
			/// <param name = "xml">The XML string.</param>
			/// <returns>The XML XPath document object model (XPathNavigator)</returns>
			public static XPathNavigator ToXPath(this string xml)
			{
				var document = new XPathDocument(new StringReader(xml));
				return document.CreateNavigator();
			}

			/// <summary>
			/// 	Decodes a Base 64 encoded value to a string using the default encoding.
			/// </summary>
			/// <param name = "encodedValue">The Base 64 encoded value.</param>
			/// <returns>The decoded string</returns>
			public static string DecodeBase64(this string encodedValue)
			{
				return encodedValue.DecodeBase64(null);
			}

			/// <summary>
			/// 	Decodes a Base 64 encoded value to a string using the supplied encoding.
			/// </summary>
			/// <param name = "encodedValue">The Base 64 encoded value.</param>
			/// <param name = "encoding">The encoding.</param>
			/// <returns>The decoded string</returns>
			public static string DecodeBase64(this string encodedValue, Encoding encoding)
			{
				encoding = (encoding ?? Encoding.UTF8);
				var bytes = Convert.FromBase64String(encodedValue);
				return encoding.GetString(bytes);
			}

		}

		public class RegexTemplates
		{
			private RegexTemplates() { }

			public const string Alpha = @"^[a-zA-Z]*$";
			public const string AlphaUpperCase = @"^[A-Z]*$";
			public const string AlphaLowerCase = @"^[a-z]*$";
			public const string AlphaNumeric = @"^[a-zA-Z0-9]*$";
			public const string AlphaNumericSpace = @"^[a-zA-Z0-9 ]*$";
			public const string AlphaNumericSpaceDash = @"^[a-zA-Z0-9 \-]*$";
			public const string AlphaNumericSpaceDashUnderscore = @"^[a-zA-Z0-9 \-_]*$";
			public const string AlphaNumericSpaceDashUnderscorePeriod = @"^[a-zA-Z0-9\. \-_]*$";

			public const string Numeric = @"^\-?[0-9]*\.?[0-9]*$";
			public const string SocialSecurity = @"\d{3}[-]?\d{2}[-]?\d{4}";
			public const string Email = @"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$";
			public const string Url = @"^^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_=]*)?$";
			public const string ZipCodeUS = @"\d{5}";
			public const string ZipCodeUSWithFour = @"\d{5}[-]\d{4}";
			public const string ZipCodeUSWithFourOptional = @"\d{5}([-]\d{4})?";
			public const string PhoneUS = @"\d{3}[-]?\d{3}[-]?\d{4}";

			public const string MAC = @"^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$";
			public const string Time = "([0-1][0-9]|2[0-3]):([0-5][0-9])";

			public const string HardCodedString = @"(""([^\""\\\n]|\\'|\\0|\\a|\\b|\\f|\\r|\\v|\\""|\\n|\\t|\\\\)*"")|(@""([^\""]|(\""\""))*"")";
		}
	}

	namespace EncodingExt
	{
		using System.Text;

		public class NullEncoding : Encoding
		{
			public static readonly NullEncoding Instance = new NullEncoding();

			private NullEncoding() { }

			public override int GetByteCount(char[] chars, int index, int count)
			{
				return 0;
			}

			public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
			{
				return 0;
			}

			public override int GetCharCount(byte[] bytes, int index, int count)
			{
				return 0;
			}

			public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
			{
				return 0;
			}

			public override int GetMaxByteCount(int charCount)
			{
				return 0;
			}

			public override int GetMaxCharCount(int byteCount)
			{
				return 0;
			}
		}

		public class EncodingEx
		{
			private static readonly Encoding _GBK;
			private static readonly Encoding _BIG5;
			private static readonly Encoding _ShiftJIS;
			private static readonly Encoding _Korea;

			static EncodingEx()
			{
				_GBK = GetCoding(936);
				_BIG5 = GetCoding(950);
				_ShiftJIS = GetCoding(932);
				_Korea = GetCoding(949);
			}

			private static Encoding GetCoding(int n)
			{
				Encoding c;
				try
				{
					c = Encoding.GetEncoding(n);
				}
				catch
				{
					c = NullEncoding.Instance;
				}
				return c;
			}

			public static Encoding GBK
			{
				get { return _GBK; }
			}

			public static Encoding BIG5
			{
				get { return _BIG5; }
			}

			private static Encoding ShiftJIS
			{
				get { return _ShiftJIS; }
			}

			private static Encoding Korea
			{
				get { return _Korea; }
			}

			public static Encoding ASCII
			{
				get { return Encoding.ASCII; }
			}

			public static Encoding BigEndianUnicode
			{
				get { return Encoding.BigEndianUnicode; }
			}

			public static Encoding Default
			{
				get { return Encoding.Default; }
			}

			public static Encoding Unicode
			{
				get { return Encoding.Unicode; }
			}

			public static Encoding UTF7
			{
				get { return Encoding.UTF7; }
			}

			public static Encoding UTF8
			{
				get { return Encoding.UTF8; }
			}

			public static Encoding GetEncoding(string name)
			{
				return Encoding.GetEncoding(name);
			}

			public static Encoding GetEncoding(int codePage)
			{
				return Encoding.GetEncoding(codePage);
			}
		}
	}

	namespace XmlExt
	{
		using System.Xml;
		using System.Xml.Linq;
		using SystemHelperLibrary.TypeHelper;

		[Serializable]
		public abstract class XmlSerializable<T> where T : XmlSerializable<T>
		{
			public string ToXml()
			{
				return XmlSerializer<T>.Xml.Serialize((T)this);
			}

			public static T FromXml(string xml)
			{
				return XmlSerializer<T>.Xml.Deserialize(xml);
			}
		}

		public class XmlSerializer<T>
		{
			public static readonly XmlSerializer<T> Xml = new XmlSerializer<T>();

			private readonly string _rootName;

			public XmlSerializer() { }

			public XmlSerializer(string rootName)
			{
				this._rootName = rootName;
			}

			private XmlRootAttribute GetXmlRootAttribute()
			{
				Type t = typeof(T);
				var xt = ClassHelper.GetAttribute<XmlTypeAttribute>(t, false);
				if (xt == null)
				{
					var xr = ClassHelper.GetAttribute<XmlRootAttribute>(t, false);
					if (xr == null)
					{
						string rn = (string.IsNullOrEmpty(_rootName)) ? t.Name : _rootName;
						xr = new XmlRootAttribute(rn);
					}
					return xr;
				}
				return new XmlRootAttribute(xt.TypeName);
			}

			public virtual string Serialize(T obj)
			{
				Type t = typeof(T);
				XmlRootAttribute xr = GetXmlRootAttribute();
				var ois = new XmlSerializer(t, xr);
				using (var ms = new MemoryStream())
				{
					ois.Serialize(ms, obj);
					byte[] bs = ms.ToArray();
					string s = Encoding.UTF8.GetString(bs);
					return s;
				}
			}

			public virtual T Deserialize(string source)
			{
				Type t = typeof(T);
				XmlRootAttribute xr = GetXmlRootAttribute();
				var ois = new XmlSerializer(t, xr);
				using (var ms = new MemoryStream())
				{
					byte[] bs = Encoding.UTF8.GetBytes(source);
					ms.Write(bs, 0, bs.Length);
					ms.Position = 0;
					return (T)ois.Deserialize(ms);
				}
			}
		}

		public sealed class XMLDocumentEx : XmlDocument
		{
			public bool AddNode(XmlNode oSource, String sName)
			{
				return AddNode(oSource, sName, null);
			}

			public bool AddNode(XmlNode oSource, String sName, String sParent)
			{
				try
				{
					if (sName != null && oSource != null)
					{
						// create the new node with given name

						XmlNode oNewNode = CreateElement(sName);
						// copy the contents from the source node

						oNewNode.InnerXml = oSource.InnerXml;
						// if there is no parent node specified, then add

						// the new node as a child node of the root node

						if (sParent != null) sParent = sParent.Trim();
						if (sParent == null || sParent.Equals(String.Empty))
						{
							DocumentElement.AppendChild(oNewNode);
							return true;
						}
						// otherwise add the new node as a child of the parent node

						else
						{
							if (!sParent.Substring(0, 2).Equals("//")) sParent = "//" + sParent;
							XmlNode oParent = SelectSingleNode(sParent);
							if (oParent != null)
							{
								oParent.AppendChild(oNewNode);
								return true;
							}
						}
					}
				}
				catch (Exception)
				{
					// error handling code

				}
				return false;
			}

			public bool AddNodes(XmlNodeList oSourceList, String sName)
			{
				return AddNodes(oSourceList, sName, null);
			}

			public bool AddNodes(XmlNodeList oSourceList, String sName, String sParent)
			{
				try
				{
					if (oSourceList != null)
					{
						// call AddNode for each item in the source node list
						// return true only if all nodes are added successfully
						int i = 0;
						while (i < oSourceList.Count)
						{
							if (!AddNode(oSourceList.Item(i), sName, sParent)) return false;
							i++;
						}
						return true;
					}
				}
				catch (Exception)
				{
					// error handling code
				}
				return false;
			}

			public bool MergeNode(XmlNode oSource, String sName)
			{
				return MergeNode(oSource, sName, null);
			}

			public bool MergeNode(XmlNode oSource, String sName, String sParent)
			{
				try
				{
					if (sName != null && oSource != null)
					{
						XmlNode theNode = null;
						// if there is no parent node specified ...
						if (sParent != null) sParent = sParent.Trim();
						if (sParent == null || sParent.Equals(String.Empty))
						{
							// if the node with specified name does not exist,add it as a child node of the root node

							theNode = SelectSingleNode("//" + sName);
							if (theNode == null)
							{
								theNode = CreateElement(sName);
								DocumentElement.AppendChild(theNode);
							}
						}
						// if the parent node is specified ...

						else
						{
							// find the parent node
							if (!sParent.Substring(0, 2).Equals("//")) sParent = "//" + sParent;
							XmlNode theParent = SelectSingleNode(sParent);
							if (theParent != null)
							{
								// if the node with specified name does not exist, create
								// it first, then add it as a child node of the parent node

								theNode = theParent.SelectSingleNode(sName);
								if (theNode == null)
								{
									theNode = CreateElement(sName);
									theParent.AppendChild(theNode);
								}
							}
						}
						// merge the content of the source node into
						// the node with specified name

						if (theNode != null)
						{
							theNode.InnerXml += oSource.InnerXml;
							return true;
						}
					}
				}
				catch (Exception)
				{
				}
				return false;
			}
		}

		public static class XmlUtils
		{
			/// <summary>
			/// Formatting an XML string (useful for Silverlight)
			/// </summary>
			/// <param name="stringXml">input XML string</param>
			/// <returns></returns>
			public static string FormatXml(string stringXml)
			{
				using (StringReader stringReader = new StringReader(stringXml))
				{
					//XDocument represents an XML Document, allowing in memory
					// processing of it (primarily via Linq).
					XDocument xDoc = XDocument.Load(stringReader);
					var stringBuilder = new StringBuilder();
					XmlWriter xmlWriter = null;
					try
					{
						var settings = new XmlWriterSettings();
						settings.Indent = true;
						settings.ConformanceLevel = ConformanceLevel.Auto;
						settings.IndentChars = " ";
						settings.OmitXmlDeclaration = true;
						//The XDocument can write to an XmlWriter. 
						//The writer formats the well-formed xml.
						//If not well-formed no formating will occur.
						xmlWriter = XmlWriter.Create(stringBuilder, settings);
						xDoc.WriteTo(xmlWriter);
					}
					finally
					{
						if (xmlWriter != null)
							xmlWriter.Close();
						if (xDoc != null)
							xDoc = null;
					}
					return stringBuilder.ToString();
				}
			}

			public static XmlDocument ToXmlDocument(this XDocument xDocument)
			{
				if (xDocument != null)
				{
					XmlDocument xmlDocument = new XmlDocument();
					using (var xmlReader = xDocument.CreateReader())
					{
						xmlDocument.Load(xmlReader);
					}
					return xmlDocument;
				}
				else
					return null;
			}

			public static XDocument ToXDocument(this XmlDocument xmlDocument)
			{
				using (XmlNodeReader nodeReader = new XmlNodeReader(xmlDocument))
				{
					nodeReader.MoveToContent();
					return XDocument.Load(nodeReader);
				}
			}

			/// <summary>
			/// Convert a fragment of xml to an xml node
			/// </summary>
			/// <param name="xmlFragment_">An xml fragment starting with an outer element</param>
			/// <returns>A node on a new xml document</returns>
			public static XmlNode FragmentToNode(string xmlFragment)
			{
				XmlDocument xd = new XmlDocument();

				using (System.IO.StringReader sr = new StringReader(xmlFragment))
				{
					xd.Load(sr);
				}

				return xd.FirstChild;
			}

			/// <summary>
			/// Escapes xml
			/// </summary>
			/// <param name="xml"></param>
			/// <returns></returns>
			public static string EscapeXml(string xml)
			{
				if (xml.IndexOf("&") >= 0)
					xml = xml.Replace("&", "&amp;");

				if (xml.IndexOf("'") >= 0)
					xml = xml.Replace("'", "&apos;");

				if (xml.IndexOf("\"") >= 0)
					xml = xml.Replace("\"", "&quot;");

				if (xml.IndexOf("<") >= 0)
					xml.Replace("<", "&lt;");

				if (xml.IndexOf(">") >= 0)
					xml.Replace(">", "&gt;");

				return xml;
			}

			/// <summary>
			/// Pretty Print the input XML string, such as adding indentations to each level of elements
			/// and carriage return to each line
			/// </summary>
			/// <param name="xmlText"></param>
			/// <returns>New formatted XML string</returns>
			public static String FormatNicely(String xmlText)
			{
				if (xmlText == null || xmlText.Trim().Length == 0)
					return string.Empty;

				String result = string.Empty;

				MemoryStream memStream = new MemoryStream();
				XmlTextWriter xmlWriter = new XmlTextWriter(memStream, Encoding.Unicode);
				XmlDocument xmlDoc = new XmlDocument();

				try
				{
					// Load the XmlDocument with the XML.
					xmlDoc.LoadXml(xmlText);

					xmlWriter.Formatting = Formatting.Indented;

					// Write the XML into a formatting XmlTextWriter
					xmlDoc.WriteContentTo(xmlWriter);
					xmlWriter.Flush();
					memStream.Flush();

					// Have to rewind the MemoryStream in order to read
					// its contents.
					memStream.Position = 0;

					// Read MemoryStream contents into a StreamReader.
					StreamReader streamReader = new StreamReader(memStream);

					// Extract the text from the StreamReader.
					String FormattedXML = streamReader.ReadToEnd();

					result = FormattedXML;
				}
				catch (Exception)
				{
					// Return the original unchanged.
					result = xmlText;
				}
				finally
				{
					memStream.Close();
					xmlWriter.Close();
				}
				return result;
			}

			/// <summary>
			/// Serializes an object to xml using the XmlSerialization.
			/// The obj must have the xml attributes used for serialization.
			/// </summary>
			/// <param name="obj"></param>
			/// <returns></returns>
			public static string Serialize(object obj)
			{
				if (obj == null)
					return string.Empty;

				XmlSerializer ser = new XmlSerializer(obj.GetType());
				System.Text.StringBuilder sb = new System.Text.StringBuilder();
				using (System.IO.StringWriter writer = new System.IO.StringWriter(sb))
				{
					ser.Serialize(writer, obj);
				}
				string xml = sb.ToString();

				return xml;
			}
		}
	}

	namespace EnumerableExt
	{
		public static class EnumerableExt
		{
			/// <summary>
			/// 	Performs an action for each item in the enumerable
			/// </summary>
			/// <typeparam name = "T">The enumerable data type</typeparam>
			/// <param name = "values">The data values.</param>
			/// <param name = "action">The action to be performed.</param>
			/// <example>
			/// 	var values = new[] { "1", "2", "3" };
			/// 	values.ConvertList&lt;string, int&gt;().ForEach(Console.WriteLine);
			/// </example>
			/// <remarks>
			/// 	This method was intended to return the passed values to provide method chaining. Howver due to defered execution the compiler would actually never run the entire code at all.
			/// </remarks>
			public static void ForEach<T>(this IEnumerable<T> values, Action<T> action)
			{
				foreach (var value in values)
					action(value);
			}

			///<summary>
			///	Remove item from a list
			///</summary>
			///<param name = "source"></param>
			///<param name = "predicate"></param>
			///<typeparam name = "T"></typeparam>
			///<returns></returns>
			/// <remarks>
			/// 	Contributed by Michael T, http://about.me/MichaelTran
			/// </remarks>
			public static IEnumerable<T> RemoveAll<T>(this IEnumerable<T> source, Predicate<T> predicate)
			{
				if (source == null)
					return Enumerable.Empty<T>();

				var list = source.ToList();
				list.RemoveAll(predicate);
				return list;
			}

			///<summary>
			/// Turn the list of objects to a string of Common Seperated Value
			///</summary>
			///<param name="source"></param>
			///<param name="separator"></param>
			///<typeparam name="T"></typeparam>
			///<returns></returns>
			/// <example>
			/// 	<code>
			/// 		var values = new[] { 1, 2, 3, 4, 5 };
			///			string csv = values.ToCSV(';');
			/// 	</code>
			/// </example>
			/// <remarks>
			/// 	Contributed by Moses, http://mosesofegypt.net
			/// </remarks>
			public static string ToCSV<T>(this IEnumerable<T> source, char separator)
			{
				if (source == null)
					return string.Empty;

				var csv = new StringBuilder();
				source.ForEach(value => csv.AppendFormat("{0}{1}", value, separator));
				return csv.ToString(0, csv.Length - 1);
			}

			///<summary>
			/// Turn the list of objects to a string of Common Seperated Value
			///</summary>
			///<param name="source"></param>
			///<typeparam name="T"></typeparam>
			///<returns></returns>
			/// <example>
			/// 	<code>
			/// 		var values = new[] {1, 2, 3, 4, 5};
			///			string csv = values.ToCSV();
			/// 	</code>
			/// </example>
			/// <remarks>
			/// 	Contributed by Moses, http://mosesofegypt.net
			/// </remarks>
			public static string ToCSV<T>(this IEnumerable<T> source)
			{
				return source == null ? string.Empty : source.ToCSV(',');
			}

		}
	}

	namespace ExceptionHelper
	{
		public static class ExceptionHelper
		{
			/// <summary>
			///   Gets the original exception which is most inner exception.
			/// </summary>
			/// <param name = "exception">The exeption</param>
			/// <returns>The original exception</returns>
			/// <remarks>
			///   Contributed by Kenneth Scott
			/// </remarks>
			public static Exception GetOriginalException(this Exception exception)
			{
				if (exception.InnerException == null) return exception;

				return exception.InnerException.GetOriginalException();
			}
		}
	}
}
